<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Delicious Reverie</title><description>Benjamin Read&apos;s code garden.</description><link>https://deliciousreverie.co.uk/</link><item><title>2018 Review / 2019 Aims</title><link>https://deliciousreverie.co.uk/posts/2018-review-2019-aims/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2018-review-2019-aims/</guid><description>Wow. 2018 was a real rollercoaster for me. This is a brief review so that I can look back on what I achieved, and set out what I hope I can achieve in web development this year. </description><pubDate>Thu, 03 Jan 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Twenty Eighteen was a whirlwind of changes for me professionally. I went from working in an agency to working for a FinTech, and the difference in those two roles and the tech stacks involved have brought me many new skills as a developer. But I still suffer anxiety with all the things I want to learn.&lt;/p&gt;
&lt;h2&gt;What I Learned &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#what-i-learned&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. Moved to JavaScript &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#1.-moved-to-javascript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve already blogged about this before, but I moved from a PHP / JS role to a fullstack JS role. That was great, but it was very hard initially. I had to put in many hours outside of work to bring myself up to speed on certain core JS concepts I&apos;d missed before, as well as learn some of the key React APIs. But I went from not knowing how to pass props down to rebuilding a fairly complex calculator. That felt like a big achievement.&lt;/p&gt;
&lt;h3&gt;2. Learned The Value of QA Engineers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#2.-learned-the-value-of-qa-engineers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The QA process in my current workplace is much more rigid. Every ticket is defined, has parameters, and is approved / rejected by a QA engineer before it goes into production. For someone who is obsessive about quality, and takes it hard when I get things wrong, having a QA Engineer has meant very few issues are raised once the code is in production. Those I answer to in the organisation have much more confidence in the development process in its entirety because of this.&lt;/p&gt;
&lt;h3&gt;3. Learned about CI Tools &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#3.-learned-about-ci-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;d used Netlify to build my static sites before, but in this new role I was able to get visibility of the pipelines that were set up by our SRE (site reliability engineering) team that built the site in a Docker container, and also ran a feature CI on our PRs. This way, we could see straight away whether code was good enough for production. I&apos;m still fascinated by this complex world and want to learn more.&lt;/p&gt;
&lt;h3&gt;4. Learned the Value of a Good Code review &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#4.-learned-the-value-of-a-good-code-review&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve had some very smart people review my code, and I&apos;ve learned a lot. For example, why destructuring is a good thing, why you wouldn&apos;t use the index as the key value in a React map function, and loads more.&lt;/p&gt;
&lt;h3&gt;5. Learned how to Use GitHub Properly &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#5.-learned-how-to-use-github-properly&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I made a grand mistake of merging to master at my new job ... but I only made it once. I broke loads of stuff and caused a world of pain for my team. Now even with personal projects it goes against the grain to commit anything to Master, even if it&apos;s a tiny change.&lt;/p&gt;
&lt;h3&gt;6. Learned the value of JIRA &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#6.-learned-the-value-of-jira&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I used to believe JIRA was this unwieldy tool that only crackpots would use. But it&apos;s so useful. We have strict definitions of where the responsibility lies for each step of the process, we can&apos;t release code into production unless is the process has been properly followed, and we also have an &quot;expedite&quot; lane so developers can focus exactly on what they need to do. Honestly it&apos;s transformed the way I work and made shipping code so much faster.&lt;/p&gt;
&lt;h2&gt;What I Want to Learn &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#what-i-want-to-learn&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;TypeScript &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#typescript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I had never seen the reason for type checking before, but now realise that a lot of the issues I&apos;ve been having all along with JavaScript can be resolved by using TypeScript. The verbosity puts me off, but I can&apos;t wait to TypeScript all my projects.&lt;/p&gt;
&lt;h3&gt;Unit &amp;amp; Integration Testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#unit-and-integration-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve never written tests before, but I want to get to the stage where I could say I would be happy to undertake a role that involved Test-Driven Development.&lt;/p&gt;
&lt;h3&gt;Docker &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#docker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Still don&apos;t understand that thing. I want to learn this dark art so that I can set up feature CI tools and production pipelines myself.&lt;/p&gt;
&lt;h3&gt;More Logic Based Development &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#more-logic-based-development&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I enjoy being the Fronted Developer, but I&apos;d also like to build a few microservices and become more comfortable with the thought process of building for stuff that isn&apos;t going to be displayed in a browser.&lt;/p&gt;
&lt;h3&gt;Advocate for Younger Developers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2018-review-2019-aims/#advocate-for-younger-developers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I see how skewed the whole development industry has become, I want to use my privilege to help others from different backgrounds to get into writing code.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2018 Review / 2019 Aims&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2019 Review / 2020 Aims</title><link>https://deliciousreverie.co.uk/posts/2019-review-2020-aims/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2019-review-2020-aims/</guid><description>This year has solidified my JavaScript knowledge, and I think I have also developed more of the holistic skills around the framework of tools and teams that surround me. But there&apos;s still a lot more I need to work on... </description><pubDate>Fri, 11 Jan 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I &lt;a href=&quot;https://deliciousreverie.co.uk/post/2018-review-2019-aims/&quot;&gt;opened last year&apos;s post&lt;/a&gt; by stating what a rollercoaster it had been for me. Well, this year has been no different; I started out the year becoming a father for the third time. That has been a wonderful, exhausting experience!&lt;/p&gt;
&lt;p&gt;Professionally too, my team went from a band of three (a developer, QA and project manager), to one of five to seven people, including another full-time developer, my personal friend and former colleague &lt;a href=&quot;https://way2adv.com/&quot;&gt;David&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;David and the other members of the team have taught me a great deal. Here are some of the main learns I think I&apos;ve achieved:&lt;/p&gt;
&lt;h2&gt;What I think I achieved last year &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#what-i-think-i-achieved-last-year&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. TypeScript &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#1.-typescript&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TypeScript was going to be one of the biggest accelarators for me in terms of the quality of my output, and I have really enjoyed using it. I started out by implementing interfaces and ended up configuring my environments with much more type safe settings by using noImplicitAny and similar.&lt;/p&gt;
&lt;h3&gt;2. Testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#2.-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Not just unit and integration, but I&apos;ve also started to plan and develop end-to-end tests as well. I&apos;ve become a lot more aware of the value of tools like Cypress and Percy, and have even started to write some components using test-driven development.&lt;/p&gt;
&lt;h3&gt;3. Docker, Kubernetes, CI tools &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#3.-docker-kubernetes-ci-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I hoped to continue my journey into DevOps / Site Reliability Engineering by learning these tools, but I&apos;ve hit a bit of a philisophical roadblock.&lt;/p&gt;
&lt;p&gt;I think I know enough about Docker to use it fairly well, and I&apos;m getting to grips with the basics of Kubernetes and CI tools (I&apos;ve been involved in writing build scripts for Jenkins and CircleCI).&lt;/p&gt;
&lt;h3&gt;4. Do more than UI Development &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#4.-do-more-than-ui-development&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I started the year by saying I wanted to do more &quot;logic-based development&quot;, however I now think those words were ill chosen. The logic of what you&apos;re building is so tied to the requirements of what you&apos;re working with that applying it to a UI or an API (or something of that nature) is equivalent.&lt;/p&gt;
&lt;p&gt;I&apos;ve been able to improve my coding chops this year so that JavaScript no longer daunts or scares me. I would no longer feel inadequate if I looked up on MDN even simple functions like map or reduce in order to achieve the task at hand.&lt;/p&gt;
&lt;p&gt;I still consider myself a Frontend Developer, but I now think that&apos;s more to do with circumstances than it is ability.&lt;/p&gt;
&lt;h2&gt;What I Want to Achieve This Year &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#what-i-want-to-achieve-this-year&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Only this week I was screensharing with our tech lead, watching him whizz around Kubernetes pods like that scene in Valerian, killing and restarting pods, chatting with the SRE team, sshing into a container and using a bash script to figure out what was going on there.&lt;/p&gt;
&lt;p&gt;I&apos;d love to have the confidence to do that. So that&apos;s the two things I&apos;m going to add first:&lt;/p&gt;
&lt;h3&gt;1. Bash scripting &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#1.-bash-scripting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&apos;s becoming clear to me there&apos;s a huge benefit to learning this. If I&apos;d known Bash, I would be able to customise my environments far beyond what I can achieve currently.&lt;/p&gt;
&lt;h3&gt;2. Continue getting to grips with Kubernetes &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#2.-continue-getting-to-grips-with-kubernetes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Kubernetes is an awesome tool, and has so many applications outside web development. I&apos;d love to at least be able to understand it&apos;s lifecycle methods and helper functions well enough to see what&apos;s going on in my stack and fix minor issues.&lt;/p&gt;
&lt;h3&gt;3. Serverless! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#3.-serverless!&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Serverless really makes sense to me. That separation of concerns with the frontend, backend and satellite services, lambda functions, and other functionality, just seems more natural and less of a cognitive overload.&lt;/p&gt;
&lt;p&gt;I&apos;d love to think that by the end of 2020 I will be able to write a Terraform script and get a system comprised of a few key pieces not only up and running but fully deployed, monitored and testable too.&lt;/p&gt;
&lt;h3&gt;4. Writing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2019-review-2020-aims/#4.-writing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One thing that has surprised me is that I really love writing about web development. This way I can help others by utilising another skill I think I have: being able to write moderately well.&lt;/p&gt;
&lt;p&gt;My relationship with Net magazine is very strong, and I&apos;d love to see if I can write a bit more of the kinds of articles that have been well received by their audience so far. This might be more difficult to achieve though because of the lack of personal time I currently have.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2019 Review / 2020 Aims&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2020 Review / 2021 Aims</title><link>https://deliciousreverie.co.uk/posts/2020-review-2021-aims/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2020-review-2021-aims/</guid><description>2020 has been ... a lot. But I&apos;ve also learned a ton of stuff. Though I can&apos;t ever say I know enough of anything, my objectives for 2021 are going to be quite different from previous years... </description><pubDate>Fri, 04 Dec 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;2020 has been ... a lot. But I&apos;ve also learned a ton of stuff. Though I can&apos;t ever say I know enough of anything, my objectives for 2021 are going to be quite different from previous years...&lt;/p&gt;
&lt;p&gt;I&apos;ve definitely not been the smartest person in the room over the last year. I&apos;ve worked with some incredible people, such as David Hewitt, David Fernandez, Romina Moya, Kate Beavis, Gerard Blanes, and many other great folk who have been good enough to explain stuff, pair code with me, and bear with my failures.&lt;/p&gt;
&lt;h2&gt;What I set out to achieve this year &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#what-i-set-out-to-achieve-this-year&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. Bash scripting &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#1.-bash-scripting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Erm, didn&apos;t really get to this a huge amount. Though I&apos;m less concerned with remembering the syntax of specific languages these days (more about that later...)&lt;/p&gt;
&lt;h3&gt;2. Continue getting to grips with Kubernetes &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#2.-continue-getting-to-grips-with-kubernetes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Oh wow ... I can&apos;t believe I added this lol. I spent only as much time with K8s this year as I had to. I know it&apos;s got it&apos;s value but the point of an abstraction is generally so you don&apos;t have to worry about it unless something goes wrong. Nothing has gone horribly wrong with our Kubernetes instances this year.&lt;/p&gt;
&lt;h3&gt;3. Serverless! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#3.-serverless!&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Serverless still really makes sense to me. I spend a bit of time during furlough following along with &lt;a href=&quot;https://serverless-stack.com/&quot;&gt;Seed&apos;s excellent tutorial at serverless-stack&lt;/a&gt;, and messing around with the serverless application framework &lt;a href=&quot;https://webiny.com/&quot;&gt;Webiny&lt;/a&gt;, which were great eye openers.&lt;/p&gt;
&lt;p&gt;&quot;I&apos;d love to think that by the end of 2020 I will be able to write a Terraform script and get a system comprised of a few key pieces not only up and running but fully deployed, monitored and testable too.&quot;&lt;/p&gt;
&lt;p&gt;Ha ha ... nope. Not that bothered by Terraform any more. It&apos;s a good enterprise system that I&apos;d gladly pick up if my job required it ... but if not, I&apos;m sticking with the Serverless framework. As with Kubernetes, it has more abstractions but less to have to worry about yourself.&lt;/p&gt;
&lt;h3&gt;4. Writing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#4.-writing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ah you know, that didn&apos;t go at all the way I&apos;d hoped! Netmag folded just when I&apos;d finished writing my first feature article for them. How sad for everyone who worked there. I&apos;m still scoping out how I might be able to continue writing, but haven&apos;t figured anything out yet.&lt;/p&gt;
&lt;h2&gt;What I Want to Achieve This Year &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#what-i-want-to-achieve-this-year&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I mentioned near the start of this post, my journey this year has involved less about deepening my understanding of tools, and more about the coreskills of a software engineer. So that&apos;s what I&apos;m aiming for primarily this year.&lt;/p&gt;
&lt;h3&gt;Advocacy and mentoring &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#advocacy-and-mentoring&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I already really enjoy advocating for others, so my role leading Zopa&apos;s Frontend Guild will continue. I hope that I can chair the group fairly and focus on the needs of our most junior and least outspoken members.&lt;/p&gt;
&lt;p&gt;Next year will also bring a new opportunity to help someone grow their career, and I relish the opportunity to be involved in that.&lt;/p&gt;
&lt;h3&gt;Follow through &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#follow-through&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One piece of feedback I received this year was that I should pursue issues that arise with third parties and other internal teams, instead of waiting for others to ask me what the next steps could be.&lt;/p&gt;
&lt;p&gt;If there&apos;s an issue I should pick it up and progress it as far as I can on my own, not leaning on others in the team to do so. Although, keeping them informed about what&apos;s happening and allowing them visibility over key events and decisions is a necessity.&lt;/p&gt;
&lt;h3&gt;Stand up for whats right &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#stand-up-for-whats-right&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;During a few exercised I wasn&apos;t able to explain clearly why my preferred approach should be adopted, over another approach that was favoured by someone who wasn&apos;t as experienced as I.&lt;/p&gt;
&lt;p&gt;I found that I struggled to make a case why it was better. If I&apos;d been more insistent I think I would have been able to politely convince the other person why my approach was the best.&lt;/p&gt;
&lt;p&gt;I still need to introspect about this a little, hopefully I can find out why I do that!&lt;/p&gt;
&lt;h3&gt;Not giving up &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#not-giving-up&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I recently paired with David on a project where we were both unfamiliar with the codebase. What impressed me is that David didn&apos;t give up, he kept pursuing different avenues to figure out what the code did and how it worked.&lt;/p&gt;
&lt;p&gt;This persistence ultimately led to us refactoring the code to achieve our objective instead of reaching out to colleagues who would have been able to help.&lt;/p&gt;
&lt;p&gt;The tricks he used to progress through the code were ones I use often, but on an unfamiliar codebase I would more than likely reach out to others earlier instead of applying those skills in this setting. I&apos;m going to try to change that perspective and persist with puzzles that I&apos;m not familiar with.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2020-review-2021-aims/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A lot of these items are here because I want to become a Senior developer, and a lot of that isn&apos;t about code but about other skills which you need to exercise.&lt;/p&gt;
&lt;p&gt;There&apos;s a lot of churn in our industry, and I&apos;m still really keen to learn things like Svelte, spend more time with Webiny, and more with fullstack apps (instead of static frontends). However that&apos;s proved to be really difficult due to the time my children need from me at this stage in their lives.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2020 Review / 2021 Aims&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2021 Review / 2022 Aims</title><link>https://deliciousreverie.co.uk/posts/2021-review-2022-aims/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2021-review-2022-aims/</guid><description>2021 continued the trend of being more challenging than any period before it, in terms of maintaining some sort of normality to our lives. However there have been some really good things too. Here&apos;s a review of what I set out to achieve and what I want to achieve this year</description><pubDate>Wed, 29 Dec 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;2021 continued the trend of being more challenging than any period before it, in terms of maintaining some sort of normality to our lives. However there have been some really good things too. Here&apos;s a review of what I set out to achieve and what I want to achieve this year&lt;/p&gt;
&lt;p&gt;When 2021 dawned I was still a Mid Level Software Engineer at Zopa. That changed in April as I took a new role as Senior JavaScript Engineer at Purple Bricks. It was a great boost for my self confidence and a fantastic team to work with. But 2022 finds me at another turning point: going from that to a split between DevRel and Fullstack Engineer at Webiny. With that change comes some reassessment of where I want my career to go in the next year.&lt;/p&gt;
&lt;h2&gt;What I wanted to achieve &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#what-i-wanted-to-achieve&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I set out to improve how I was doing in these following areas:&lt;/p&gt;
&lt;h3&gt;Advocacy and mentoring &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#advocacy-and-mentoring&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At the start of the year I was trying to build better bridges between disparate teams at Zopa. I enjoyed that a lot. My desire to do more of that got a massive boost when I joined Purple Bricks as one of their 3 Seniors.&lt;/p&gt;
&lt;p&gt;It was fantastic to not only mentor various members of the team, but also help set the direction of the whole chapter. I also enjoyed delivering a training course to the business on GraphQL, and building what was for me the most complex system I&apos;ve built: a messaging app.&lt;/p&gt;
&lt;p&gt;But what I remember most fondly was the moment I told my colleague Helen that this was my first Senior role. &quot;Wh ... what?!&quot; was her response! I&apos;ve been keen to demonstrate that I don&apos;t always have the answers (or even the best answers), and that I often don&apos;t know what I&apos;m doing, and I think that has actually boosted the confidence others have in me, rather than the opposite.&lt;/p&gt;
&lt;h3&gt;Follow through &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#follow-through&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Yep, I definitely have had to give this some attention. But as most of the follow through has been on making sure more junior teams aren&apos;t still stuck after I&apos;ve left them with a solution. I think I&apos;ve got better at this, at least I hope I have!&lt;/p&gt;
&lt;h3&gt;Not giving up &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#not-giving-up&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;m still bad at this! I&apos;m more persistent definitely, and I&apos;m a lot more comfortable at diving through code I don&apos;t understand and finding ways around problems. But I also realise my personal style is a lot different to my friend David&apos;s, and I&apos;ve become more comfortable with my own aptitudes.&lt;/p&gt;
&lt;p&gt;Aside from these, I&apos;ve really got used to digging into pipeline problems, environment issues, using Kubernetes and Helm, as well as getting better at Javascript in general, routing, graphql, and different rendering options.&lt;/p&gt;
&lt;h2&gt;What I want to achieve this year &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#what-i-want-to-achieve-this-year&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There wasn&apos;t much technological focus in last years&apos; ambitions, but I know for certain that there are going to be some strong tech challenges this year!&lt;/p&gt;
&lt;h3&gt;Fullstack &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#fullstack&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve been working a lot with NextJS applications at Purple Bricks. That has helped me build a better mental model about static, server and client rendering, and the benefits and hazards of each. Also, I have come to really enjoy the flexibility of NextJS with each of these methods.&lt;/p&gt;
&lt;p&gt;But I still have been predominantly working on the client. I&apos;m really looking forward to working with lambdas at Webiny, and I hope it&apos;ll really help me build my understanding of server side.&lt;/p&gt;
&lt;h3&gt;Serverless &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#serverless&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that I understand the principles and benefits of fullstack applications, I can&apos;t wait to discover more about serverless, and how it solves some of these problems. I anticipate it&apos;ll have it&apos;s own set of caveats too, and I can&apos;t wait to understand these more. I&apos;m also looking forward to understanding more about AWS service and APIs.&lt;/p&gt;
&lt;h3&gt;State management &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#state-management&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of Webiny&apos;s applications is a page builder, with a lot of heavy state management using Recoil. It&apos;s going to be really great to get more into a state-heavy application and learn more about that world.&lt;/p&gt;
&lt;h3&gt;Content creation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#content-creation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As of the time of writing, I have never made a TikTok video. I think this is about to change with my taking on a part DevRel role at Webiny. That seems really scary to me at the minute since I know nothing about video editing let alone how to tell a story in less than 1 minute.&lt;/p&gt;
&lt;h3&gt;Community building &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2021-review-2022-aims/#community-building&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the one I&apos;m really looking forward to. Webiny has so much potential. I can&apos;t wait to see where the team take this product. There&apos;s already a small community with some really great contributors and users, I&apos;m hoping that I can help blow that up to a much larger audience in the next year.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2021 Review / 2022 Aims&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2023 Site Rebuild</title><link>https://deliciousreverie.co.uk/posts/2023-site-rebuild/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2023-site-rebuild/</guid><description>Sorry to those people who subscribe to my RSS feed, I had a bug which I couldn&apos;t fix before I re-launched deliciousreverie.co.uk, but which I&apos;ve resolved now. Yes, I&apos;ve rebuilt my site again. This is the fifth iteration. Here&apos;s why.</description><pubDate>Wed, 27 Sep 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Sorry to those people who subscribe to my RSS feed, I had a bug which I couldn&apos;t fix before I re-launched deliciousreverie.co.uk, but which I&apos;ve resolved now.&lt;/p&gt;
&lt;p&gt;Yes, I&apos;ve rebuilt my site &lt;em&gt;again&lt;/em&gt;. This is perhaps the fifth iteration of it ... let me see, yes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Perch&lt;/li&gt;
&lt;li&gt;Hugo&lt;/li&gt;
&lt;li&gt;Gatsby&lt;/li&gt;
&lt;li&gt;Astro (Nx monorepo, Webiny CMS backend)&lt;/li&gt;
&lt;li&gt;Astro (Markdown)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The main driver this time is that I broke my production instance of Webiny CMS by trying to deploy a bunch of upgrades which I should have made gradually.&lt;/p&gt;
&lt;p&gt;But I also was struggling to manage the complexity of the CMS plus the monorepo in my spare time. It&apos;s no joke when you&apos;ve got 3 kids.&lt;/p&gt;
&lt;p&gt;Also, for all the goodness that Nx brings to a project, I was held back a little; I wanted to adopt Astro 3.0 early because of the view transitions API, but I couldn&apos;t because the Nx plugin for that was pinned to version 2.&lt;/p&gt;
&lt;p&gt;This time, the site is super simple. And it also means that I&apos;m ditching the plan I had to keep building blogging sites. Instead I&apos;m going to let the domains lapse or leave them as they are.&lt;/p&gt;
&lt;p&gt;Again it&apos;s the time thing, but also as I&apos;ve moved into a new role I&apos;ve been playing with some more serious JavaScript stuff and want to focus on doing some more experimentation with the things I&apos;ve learned, as well as starting up another class to teach kids how to code.&lt;/p&gt;
&lt;p&gt;I&apos;ve got a few articles planned, including a write up of how I made a media server, and another one about web workers and cache storage APIs.&lt;/p&gt;
&lt;p&gt;Hopefully I&apos;ll get some time to write those soon!&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2023 Site Rebuild&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>20MinJS: Serverless JavaScript: What, Where and Why</title><link>https://deliciousreverie.co.uk/posts/20minjs-serverless-javascript-what-where-why/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/20minjs-serverless-javascript-what-where-why/</guid><description>I appeared on the podcast 20 Min JS with Fernando Doglio to talk about Serverless as a concept, and how developers can maximize their time and ship deliverables faster by adopting serverless. </description><pubDate>Tue, 20 Sep 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I appeared on the podcast 20 Min JS with Fernando Doglio to talk about Serverless as a concept, and how developers can maximize their time and ship deliverables faster by adopting serverless.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:20MinJS: Serverless JavaScript: What, Where and Why&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>4 things that I learned from counselling training</title><link>https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/</guid><description>When I was younger, I studied a form of Cognitive Behavioural therapy called Counselling. I studied for over 2 years but never took it up as a career. Even so, there are some valuable lessons that continue to help me in my web development career. Here are four of them.</description><pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Most of us have moments in life where we question ourselves, often forced into being by some personal crisis or difficult situation. During one of these I enrolled on a course to learn Counselling, a form of Cognitive Behavioural therapy that is popular in the NHS and privately in the UK. I studied for about two years, and afterwards took a course in abnormal psychology. Even though I never qualified to become a counsellor, it&apos;s one skill I use often, even in web development.&lt;/p&gt;
&lt;p&gt;Here are some of the main takeaways I gleaned from my course:&lt;/p&gt;
&lt;h2&gt;1. Drink more water &lt;a href=&quot;https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/#1.-drink-more-water&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It amazed me how much of our psychology changes with our eating and drinking habits. I&apos;m not talking about drugs or stimulants here: my tutor noted that we often compensate for tiredness by drinking coffee, however a good glass of water would often have a much bigger impact.&lt;/p&gt;
&lt;p&gt;Similarly with getting sufficient sleep: if we get 7.5 hours or less, our thinking is impaired. The less we sleep, the more irrational our thinking becomes. I realized the truth of this statement most clearly one occasion when I hadn&apos;t slept for three nights, drove my car, and crashed it into a truck.&lt;/p&gt;
&lt;p&gt;If you&apos;re having an issue with some code, take a break. Have a nap. Drink some more water. It amazes me at how often this helps me to solve coding problems.&lt;/p&gt;
&lt;h2&gt;2. Everyone has different perspectives &lt;a href=&quot;https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/#2.-everyone-has-different-perspectives&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You&apos;re on the way to a close friend or family members&apos; wedding, driving down the motorway, and running late. You see a puppy on the side of the road, in distress, clearly malnourished.&lt;/p&gt;
&lt;p&gt;What would you do?&lt;/p&gt;
&lt;p&gt;Some people would drive past: taking care of the dog would make them late for the wedding; something they&apos;ve been planning for a while. Others wouldn&apos;t hesitate to stop to help the stranded animal. In fact, they might only attend the wedding once the dog has been safely cared for, no matter the time that might take.&lt;/p&gt;
&lt;p&gt;Everyone has a reason for what they do; and these perspectives often drive our decisions. The person who stopped to pick up the dog wasn&apos;t being inconsiderate to their friend who was getting married; they simply judged that another had a greater need at that moment.&lt;/p&gt;
&lt;p&gt;Conversely, the person who ignored the dog wasn&apos;t being heartless: they merely have different drivers for their decisions.&lt;/p&gt;
&lt;p&gt;Some people have a preference to write with the right-hand, others the left. We&apos;re all inherently have these preferences for our personalities too. We can train ourselves another way, like a left-handed person can train themselves to write with the right hand, but our overriding preference doesn&apos;t really go away.&lt;/p&gt;
&lt;p&gt;This taught me that we should respect others&apos; choices, and stop judging them, they have good reasons for making their decisions. They are also their decisions, not ours.&lt;/p&gt;
&lt;p&gt;We should take into consideration these perspectives when we&apos;re writing software. We should also be writing software with other people - not necessarily developers, but other people invested in the outcome. Because if you&apos;re writing software in isolation, how can you take into consideration other people&apos;s ways of thinking, or even be aware of things like their skin colour or facial features? What about their physical or cognitive disabilities?&lt;/p&gt;
&lt;h2&gt;3. Psychology theory is just theory &lt;a href=&quot;https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/#3.-psychology-theory-is-just-theory&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the most famous psychologists, Sigmund Freud, was obsessed by evolution, and therefore the base instincts a person has. This led to some pretty interesting ideas about the activity of the subconscious, but also some pretty horrific viewpoints on racism.&lt;/p&gt;
&lt;p&gt;Carl Jung was a natural successor, and I often think of him as ... kinder ... to human nature. He&apos;s the one who came up with cognitive behavioural theory (CBT), that a person can change if they have enough awareness and motivation.&lt;/p&gt;
&lt;p&gt;Isabel Briggs-Myers and her mother Katherine Cook-Briggs subsequently came up with the Myers-Briggs Type Indicator (MBTI), which suggests there are four differentiators (like a preference for writing with the left-hand or right) that combine to form a persons&apos; personality. They tested loads of people on this, and quite often the results were a remarkably good fit. Their ideas form the basis for a lot of personality tests you see today. However, because nobody knows what is really going on in our heads, it&apos;s still just a theory. We can&apos;t say it&apos;ll fit every individual. For example, by suggesting a person has certain personality traits, are they more prone to adapting to show those traits? Or to not show them over time? I&apos;ve taken the same MBTI test with a 10 year gap. My personality type the second time was significantly different to the first.&lt;/p&gt;
&lt;p&gt;As developers, we can be tempted to strictly categorise and compartmentalise everything - specifically people, but other things too. But these will ultimately break down and become less relevant. Sometimes you just have to go with what works.&lt;/p&gt;
&lt;h2&gt;4. Just listen &lt;a href=&quot;https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/#4.-just-listen&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most of the time, the most powerful thing you can do to help someone is to listen to them. As much as it gets repeated on social media, memes and other places, actually, really, attentively listening to someone is a skill we often lack.&lt;/p&gt;
&lt;p&gt;The main thing that gets in the way is ego: we strongly desire to share our experiences, our solutions, to validate ourselves, or to compete with another person.&lt;/p&gt;
&lt;p&gt;But once we let that go, instead focusing on the needs of the other person, we put ourselves in a place to really help them.&lt;/p&gt;
&lt;p&gt;What I mean by really listening is active listening: hearing what they say and then validating it, either by reflecting it back to them (using different words to say what they just said), or asking pertinent questions. If you didn&apos;t understand what someone said, don&apos;t be afraid to ask them, &quot;what did you mean by that?&quot;, or check your understanding by reflecting back and welcoming their explanations.&lt;/p&gt;
&lt;p&gt;This can help us in many aspects of web development: at the planning stage when a feature is being explained to us, when listening to feedback on a feature we&apos;ve just written, or when doing user interaction research with customers.&lt;/p&gt;
&lt;h2&gt;How does any of this help me in my web development career? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/4-things-learned-from-counselling-training/#how-does-any-of-this-help-me-in-my-web-development-career&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some people take up software engineering to get away from people. However, if you&apos;re tempted to do that I think you&apos;d probably be disappointed. We primarily engineer software to help people, and people are intrinsically involved in deciding what we build, and how we build it.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:4 things that I learned from counselling training&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2016 Review / 2017 Aims</title><link>https://deliciousreverie.co.uk/posts/2016-review-2017-aims/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2016-review-2017-aims/</guid><description>2016 as a year was as unconventional as they come. Globally there have been some massive shifts politically, socially and in other ways. My life too has taken some pretty interesting turns. I&apos;m following suit here by posting a quick review of my year and what I hope I can achieve in 2017 from a professional perspective.</description><pubDate>Fri, 12 May 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;2016 as a year was as unconventional as they come. Globally there have been some massive shifts politically, socially and in other ways. My life too has taken some pretty interesting turns. I&apos;m following suit here by posting a quick review of my year and what I hope I can achieve in 2017 from a professional perspective.&lt;/p&gt;
&lt;p&gt;When I look back at the start of 2016 it is with a huge amount of mixed feelings. Hannah and I were still getting used to the sleepless nights with Morgan, who was just then a year old. We&apos;d also recently got told to move out of our house with 4 weeks notice. That proved to be incredibly stressful.&lt;/p&gt;
&lt;h2&gt;The Summit Media project &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#the-summit-media-project&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From a professional perspective, I was doing some of the best work of my career for Ech Design on the Summit website, a monster Wordpress build with multiple custom post types, and some pretty involved animations. The deployment process was also a bit of a minefield for a team that had only just started using git.&lt;/p&gt;
&lt;p&gt;That build nearly killed the 4 of us, especially Neil and Phil, the project leads, but it&apos;s something we are all incredibly proud of even today. It was nice to have the confidence and gratitude of the Summit team after a very intense 4 / 5 month design and build period.&lt;/p&gt;
&lt;p&gt;It was also my first introduction to using a slew of new technologies, including Composer, Atlassian JIRA, a range of new animation techniques and git branching &amp;amp; deployment strategies.&lt;/p&gt;
&lt;h2&gt;Moving on from Ech &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#moving-on-from-ech&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Until late in October I was still contracting at Ech, a great team with some exciting ambitions. I&apos;m looking forward to hearing what they achieve in the future. I was sad to say farewell after almost a year there to go and work at an agency, a move which was conducive to a better work / life balance (because it&apos;s a much shorter commute).&lt;/p&gt;
&lt;p&gt;Looking back, I certainly was pushed to move my skills forwards, at times much further than I believed I could. I also was able to leave Ech having imparted to them some useful processes that I had acquired already (kanban boards for project management, git for version control, and working locally instead of via ftp).&lt;/p&gt;
&lt;p&gt;I also moved to an agency for the chance to work with the current team and their excellent in-house Wordpress theme. This theme has many fantastic features, including the fact that it uses OO PHP and has command-line interface similar to Laravel&apos;s Artisan tool for making meta fields, widgets and custom settings area.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ origin make:metabox mymetabox
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one thing that really got to me about it&apos;s nearest competitor, Elliot Condon&apos;s Advanced Custom Fields, is that you can create everything in the theme in code. This speeds up my development time exponentially, to the point that I can create an entire theme in as few as 3 days.&lt;/p&gt;
&lt;h2&gt;Aims for 2017 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#aims-for-2017&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few things I had in my goals list which are underway already include the following:&lt;/p&gt;
&lt;h3&gt;1. Build a home web server &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#1.-build-a-home-web-server&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After a few false starts (I initially used Debian, but struggled without the support as a beginner at this), my home server is up and running . The main reasons for wanting to do this is because we currently maxed out on iCloud storage and don&apos;t want to pay increasing amounts ad infinitum to store &amp;amp; share our stuff.&lt;/p&gt;
&lt;p&gt;It was also a good task to help me discover more about servers and the LAMP stack in general, which will help me understand my role and how it connects to other roles in the workplace.&lt;/p&gt;
&lt;h3&gt;2. Get to grips with SVGs and SVG animations &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#2.-get-to-grips-with-svgs-and-svg-animations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I really believe SVGs and animations are the future of the web. There&apos;s an insane amount going on with these at the moment, and I&apos;ve built sites using some rudimentary animations. Unfortunately, none of them have gone live yet. I would really like to showcase some of what I&apos;ve done and find out new ways of using animations to tell stories on websites.&lt;/p&gt;
&lt;h3&gt;3. Graduate to Laravel &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#3.-graduate-to-laravel&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve already had the opportunity at an agency to start learning Laravel, and already love it. I think this could quickly develop into a standalone blog post (umm, yep, it probably will) so I won&apos;t say anything more about it, other than I know I&apos;m going to love the framework. A colleague has already told me &quot;learning laravel will make you a better Wordpress developer&quot;. I can already see the truth of that statement.&lt;/p&gt;
&lt;h3&gt;4. Design More &lt;a href=&quot;https://deliciousreverie.co.uk/posts/2016-review-2017-aims/#4.-design-more&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one is a bit more of a vague idea than a concrete goal:- I only designed one website in 2016 and think I&apos;m losing my sensitivity to design a bit. I want to get that back a little so that I can continue to break down the siloed approach to building websites which is sadly so prevalent still.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2016 Review / 2017 Aims&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>6 reasons why we chose Nx as our monorepo management tool</title><link>https://deliciousreverie.co.uk/posts/6-reasons-why-we-chose-nx/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/6-reasons-why-we-chose-nx/</guid><description>Nx is a modular build framework for architecting and maintaining code projects. Using it means you configure once, then just focus on build your tools and apps. Nx can effectively manage the configuration around APIs, micro frontends and libraries of tools, so you don’t have to consciously think of this step each time it comes to building a new project. This is an article I wrote for the Purple Bricks tech blog on the subject. </description><pubDate>Thu, 23 Sep 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Sharing code across multiple disparate projects, at scale, can be a problem for many organisations.&lt;/p&gt;
&lt;p&gt;The challenge is to eliminate code duplication and unify user interface design patterns, whilst at the same time providing (hopefully) speedier development for engineers.&lt;/p&gt;
&lt;p&gt;There are a few ways to tackle this issue. In 2017, we settled on a monorepo pattern that facilitated Yarn workspaces. We had hoped this would provide the benefits above, but in reality we found that not a lot of code sharing was happening, especially across our frontend projects.&lt;/p&gt;
&lt;p&gt;This was because each project would have its own package.json, its own unique pipeline, and, due to its own custom React implementation, it’s own UI library.&lt;/p&gt;
&lt;p&gt;So, in 2019, we decided to rethink the problems we were having. We took a look at the tools available and found that a relatively new tool, &lt;a href=&quot;https://nx.dev/&quot;&gt;Nx from Nrwl&lt;/a&gt;, already had support for some of our common tools.&lt;/p&gt;
&lt;p&gt;We started a new monorepo using this tool that we hoped would eventually facilitate all of our initial requirements.&lt;/p&gt;
&lt;p&gt;First of all, what problems is Nx solving for us?&lt;/p&gt;
&lt;h1&gt;What problems does Nx Solve?&lt;/h1&gt;
&lt;p&gt;Nx is a smart and extensible build framework to help you architect, test, and build at any scale&lt;/p&gt;
&lt;p&gt;Nx is a modular build framework for architecting and maintaining code projects. Using it means you configure once, then just focus on build your tools and apps. Nx can effectively manage the configuration around APIs, micro frontends and libraries of tools, so you don’t have to consciously think of this step each time it comes to building a new project.&lt;/p&gt;
&lt;p&gt;It facilitates a consistency of output that is difficult to achieve otherwise. With Nx, we have gained the following benefits:&lt;/p&gt;
&lt;h2&gt;1. Standardised libraries&lt;/h2&gt;
&lt;p&gt;Each of our microfrontends use the same version of NextJS, React, TypeScript (with one established ruleset), Jest, and Cypress. Each of our UI libraries utilises the same Storybook.&lt;/p&gt;
&lt;p&gt;This means that instead of having to make decisions about what tools you’re going to use, Nx already provides them for you. As well as avoiding possibly lengthy and heated discussions about which tools developers might want to use, there’s already a standard in place for others to follow.&lt;/p&gt;
&lt;p&gt;The other benefit of standardising libraries like this is that it’s much easier for developers to dip into different apps and libraries, because they’re all much the same.&lt;/p&gt;
&lt;h2&gt;2. Code sharing&lt;/h2&gt;
&lt;p&gt;Having a monorepo also helps with onboarding. As soon as a new developer pulls down the repo, they have access to a wealth of information about our coding standards and they can immediately start to learn our coding style and what our approach to certain problems might be.&lt;/p&gt;
&lt;p&gt;New developers also have better discoverability around existing libraries that we have built, so it’s easier for them to utilise these already built tools instead of having to ask whether something they need already exists in another repo somewhere.&lt;/p&gt;
&lt;h2&gt;3. Code standards&lt;/h2&gt;
&lt;p&gt;Do you want to use spaces or tabs? Should we enable noImplicitAny? Should we use rem, em or px? None of these discussions happen, or they happen less often, because we can enforce the same code standard across all of our projects, and automatically alert developers and block PRs that don’t follow these standards.&lt;/p&gt;
&lt;p&gt;It makes life a lot easier.&lt;/p&gt;
&lt;h2&gt;4. Deployments&lt;/h2&gt;
&lt;p&gt;We can also standardise deployments using Nx. We have one single pipeline for all of our apps, which means we have less overhead in maintaining that pipeline.&lt;/p&gt;
&lt;p&gt;When deploying, we check which applications have beenaffected by the code changes being made by utilising the command nx affected. This allows us to run the test suites for these applications. If all of these pass, we know we have a valid build and can have relative confidence that releasing code that alters several applications at once won’t result in any breaking changes.&lt;/p&gt;
&lt;h2&gt;5. Versioning&lt;/h2&gt;
&lt;p&gt;Working at previous organisations, I have encountered issues with different versions of libraries being used by different applications within the organisation’s overall codebase. This means that the UI can be significantly different across different areas of the business.&lt;/p&gt;
&lt;p&gt;This can also result in deprecated functions still being used months or years after they have been retired.&lt;/p&gt;
&lt;p&gt;With Nx, all of our apps consume a single version of a library, and that’s updated every time there’s a merge to master.&lt;/p&gt;
&lt;p&gt;This means there’s less risk of unexpected breaking changes, and no sprints wasted bumping library versions: it’s all done once, at the same time the library is modified. This could also help changes to the API of a library to be more robust, because it’s being consumed immediately, and not after some time has already passed and other teams have begun to implement the change.&lt;/p&gt;
&lt;h2&gt;6. Development Experience&lt;/h2&gt;
&lt;p&gt;In previous iterations of our development workspaces, it could take up to 3 days to deploy a new application. With Nx, it can take a few hours. All you need is to run one command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nx g @nrwl/next:app becky-quotes &amp;amp;&amp;amp; yarn generate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have configured this to build a new application that has helm charts for the deployment, as well as a NextJS application and a Cypress e2e test suite.&lt;/p&gt;
&lt;p&gt;This also helps with onboarding as well as assists us with automated dependency management.&lt;/p&gt;
&lt;p&gt;We use Renovate to raise nightly PRs to update dependencies. We have a requirement of 100% unit test code coverage, which is instrumental in ensuring the changes this automated system makes don’t break anything.&lt;/p&gt;
&lt;p&gt;These benefits have clearly saved us a lot of time and we’re extremely happy with the results. But it hasn’t all been plain sailing. Aside from all the benefits, let’s look at one of the main challenges we’ve faced…&lt;/p&gt;
&lt;h1&gt;Poor library strategy&lt;/h1&gt;
&lt;p&gt;Here’s our dependency graph. As you can see, all of the apps depend on a single library: our shared UI components.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This means that when we update something in this library, or when writing a new component here, Nx sees that all of these apps have the dependency of that single library, and it initiates tests for all of those applications.&lt;/p&gt;
&lt;p&gt;We have 14 currently, so this task can take a great deal of time. This is especially true in our pipelines, which as a consequence sometimes experience timeout issues.&lt;/p&gt;
&lt;p&gt;Our solution to this is to have much smaller libraries. Instead of one UI library, we are going to have one library per component. This, at first, might seem counterintuitive, but it means that changing components, and creating new ones, can be done much more quickly. All of the components can still be consumed with a single Storybook instance. And some of the libraries will of course consume some of the other libraries (the base theme for example).&lt;/p&gt;
&lt;h1&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Nx has been a terrific investment for Purplebricks. It hasn’t all been straightforward, and we have faced challenges, but I would definitely recommend Nx as a great way of managing projects — as it makes the jobs we do far easier by comparison to the alternatives available.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:6 reasons why we chose Nx as our monorepo management tool&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2022 Website Rebuild</title><link>https://deliciousreverie.co.uk/posts/2022-website-rebuild/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/2022-website-rebuild/</guid><description>I&apos;ve just relaunched my website with an entirely new stack, here&apos;s what it consists of and why I&apos;ve made these technical decisions.</description><pubDate>Fri, 25 Nov 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Around a year ago I rebuilt this website using &lt;a href=&quot;https://www.11ty.dev&quot;&gt;Eleventy&lt;/a&gt;, the static site generator. I did this because my existing site was using &lt;a href=&quot;https://www.gatsbyjs.com&quot;&gt;Gatsby&lt;/a&gt; but was [using a plugin which removed the JavaScript in a way that caused errors on the site](https://www.gatsbyjs.com/plugins/gatsby-plugin-no-javascript/). I didn&apos;t want to put the JavaScript-heavy build back, but the broken links were causing errors in the console and I knew it  wasn&apos;t the best approach to a JavaScript-less Gatsby site, but at the time there was no alternative.&lt;/p&gt;
&lt;p&gt;Eleventy is quite a unique tool. Whilst I can see it&apos;s appeal for some, I found the way it consumes data and then injects it back in at build time a bit of a black box. At the time I rebuilt it, I was hoping to use the revolutionary plugin Slinkity to provide client-side JavaScript so I build build back features such as animations and search. However, by the time I launched my site, the author of Slinkity had moved to Astro, and development had slowed.&lt;/p&gt;
&lt;h2&gt;Rebuilding in Astro&lt;/h2&gt;
&lt;p&gt;I had determined that I would rebuild the site a second time in &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;, but it was early days for that tool as well, so I&apos;ve sat on it for the time being. However, in recent weeks I&apos;ve really needed to make some serious changes to the site so I could showcase some of my recent work at Webiny, and Astro had reached v1, so it seemed a good time to switch.&lt;/p&gt;
&lt;p&gt;Therefore, the frontend is built in Astro. As yet there&apos;s no JavaScript, but I&apos;m planning to use &lt;a href=&quot;https://svelte.dev&quot;&gt;Svelte&lt;/a&gt; because of it&apos;s developer experience and unique approach to providing interactivity. However, I could very easily add components that are built in other frontend frameworks. This agnostic use of frontend rendering libraries is a powerful enabler, and one I want to leverage.&lt;/p&gt;
&lt;h2&gt;Styling with Tailwind (and others)&lt;/h2&gt;
&lt;p&gt;I&apos;ve used &lt;a href=&quot;https://tailwindcss.com&quot;&gt;Tailwind&lt;/a&gt; with &lt;a href=&quot;https://daisyui.com&quot;&gt;DaisyUI&lt;/a&gt; for the most part because of the way it compiles away unused CSS. At most I&apos;ve used 4 Tailwind classes on an element. My reasoning for this was that if I&apos;m using a lot of classes, I&apos;m probably doing Tailwind wrong. I&apos;ve got more to say on this subject so I&apos;ll save it for a separate article. But suffice to say for now that using Tailwind with DaisyUI is a great combination because you have access to a library without being tied to semantic elements and which are also tree-shakeable. DaisyUI is a Tailwind plugin, so elements are only compiled into the CSS bundle when you use them, same as Tailwind.&lt;/p&gt;
&lt;p&gt;I haven&apos;t exclusively used Tailwind and Daisy, because I think every system is at some point going to fall short of what you need; in my case it was the ability to &lt;a href=&quot;https://github.com/endymion1818/personal-frontends-monorepo/blob/535a0c7b02f59bf9a60da95820b1418ff58be267/libs/rich-text-renderer/src/lib/RichTextRenderer.astro#L64&quot;&gt;underline links that are inline in the content&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Big shoutout to Salma (&lt;a href=&quot;https://twitter.com/whitep4nth3r?s=21&amp;amp;t=fuSkdsKWbtIpZBQwn-mKrg&quot;&gt;@whitep4nth3r&lt;/a&gt;) for &quot;roasting&quot; my site on her Twitch stream which identified loads of layout, typography and spacing issues.&lt;/p&gt;
&lt;h2&gt;Webiny for the Content Store&lt;/h2&gt;
&lt;p&gt;I&apos;m pleased to use Webiny to store content. I manually moved all of my articles over to my new &lt;a href=&quot;https://www.webiny.com&quot;&gt;Webiny&lt;/a&gt; instance which is deployed on AWS using Webiny&apos;s &lt;a href=&quot;https://www.pulumi.com&quot;&gt;Pulumi&lt;/a&gt; integration. Webiny is an eminently hackable CMS which is why I like it. Because it uses &lt;a href=&quot;https://github.com/editor-js&quot;&gt;EditorJS&lt;/a&gt; in the backend, I was able to customize it by &lt;a href=&quot;https://github.com/editor-js/code&quot;&gt;adding a plugin for code blocks&lt;/a&gt;. Unfortunately there seems to be a problem using some of the other plugins (embeds and inline code blocks) which I want to investigate.&lt;/p&gt;
&lt;p&gt;It was tricky to figure out how to render this content in Astro. Rather than using Prism,  I opted for &lt;a href=&quot;https://shiki.matsu.io&quot;&gt;Shiki&lt;/a&gt; because it&apos;s native to Astro and would mean adding one less dependency.&lt;/p&gt;
&lt;p&gt;Actually I really like Shiki&apos;s API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import shiki from &apos;shiki&apos;

const { code } = Astro.props

let codeOutput

await shiki.getHighlighter({
  theme: &apos;monokai&apos;,
}).then(highlighter =&amp;gt; {
  codeOutput = highlighter.codeToHtml(code, { lang: &apos;jsx&apos; })
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is using Astro&apos;s top-level await. Unfortunately the EditorJS plugin doesn&apos;t allow you to store a language by default. This is something I might try to change soon.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Although Webiny&apos;s editing interface isn&apos;t as nice to use as others, it&apos;s a great tool that can scale with the content I produce. Since all of the blogs I look after only have around 100-200 records, I&apos;m planning to use one Webiny instance for all of them. I think that will ultimately mean I need to migrate from the current implementation (DynamoDB only) to one which uses ElasticSearch to index and retrieve records. I also may need to pay for a license to use the multisite feature, but I&apos;ll worry about that later.&lt;/p&gt;
&lt;p&gt;Also, I&apos;ve been very lazy about retrieving records in my frontends. Instead of utilizing the built-in cursor based pagination from the GraphQL API, I&apos;ve just increased the limit of retrieved articles to 200 and grabbed all of them in one API call. I realize I shouldn&apos;t really do this so I&apos;ve &lt;a href=&quot;https://github.com/endymion1818/personal-frontends-monorepo/blob/535a0c7b02f59bf9a60da95820b1418ff58be267/apps/deliciousreverie/src/pages/posts/index.astro#L42&quot;&gt;added something to the codebase that will case builds to fail if there are over 200 records&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have found that writing in Markdown isn&apos;t fun anymore either. I&apos;m hoping using this interface will therefore encourage me to write more often.&lt;/p&gt;
&lt;h2&gt;Code Management&lt;/h2&gt;
&lt;p&gt;One of my objectives with this rebuild was to consolidate all of my content-heavy websites. I manage and write content for &lt;a href=&quot;https://freebabylon5.com&quot;&gt;https://freebabylon5.com&lt;/a&gt; and &lt;a href=&quot;https://www.discovermikeoldfield.info&quot;&gt;https://www.discovermikeoldfield.info&lt;/a&gt;, and I want to build some more sites soon. However, each of these sites is built on different technologies. I was a hoping that I could consolidate all of these into one monorepo using &lt;a href=&quot;https://nx.dev&quot;&gt;Nx&lt;/a&gt; so I could re-use the setup and different shared elements, like the header, footer and rich text renderer. I &lt;a href=&quot;https://www.netlify.com/blog/2020/04/21/deploying-nx-monorepos-to-netlify/&quot;&gt;followed this tutorial to set up Netlify deploys from the monorepo&lt;/a&gt;, but as yet I haven&apos;t tested it with a seconds site, so I&apos;m not sure how that might go.&lt;/p&gt;
&lt;p&gt;This means there will be one monorepo for frontend code, one for the backend code, and I can iterate on the two as I go along. This setup is pleasing from a code re-use perspective as well as saving me time that I can spend with my family, whilst at the same time allowing me to enjoy my hobby of writing.&lt;/p&gt;
&lt;h2&gt;The future&lt;/h2&gt;
&lt;p&gt;I&apos;m hoping to add some features in the near future&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Search with Lunr.JS&lt;/li&gt;
&lt;li&gt;Add back the wistful animations&lt;/li&gt;
&lt;li&gt;Inline embeds and Code blocks&lt;/li&gt;
&lt;li&gt;Fetching data from GitHub&lt;/li&gt;
&lt;li&gt;Fetching and rendering the latest video from my YouTube channel&lt;/li&gt;
&lt;li&gt;Rendering previous &amp;amp; next links on articles&lt;/li&gt;
&lt;li&gt;Using a self-hosted alternative to Calendly&lt;/li&gt;
&lt;li&gt;Using self-hosted analytics&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In addition to this, as I mentioned, I want to scale out this stack to provide unique experiences for each of my websites.&lt;/p&gt;
&lt;p&gt;Also I&apos;ve just discovered that creating a new build on Netlify is cached by Nx, so when I want to publish a new article (which doesn&apos;t involve any code changes), I need to alter the build settings to include the skip Nx Cache command.  that&apos;s something I need to dig into as well as setting up a CI-CD pipeline for the backend.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Both the &lt;a href=&quot;https://github.com/endymion1818/personal-frontends-monorepo&quot;&gt;frontend&lt;/a&gt; and the &lt;a href=&quot;https://github.com/endymion1818/backends-webiny&quot;&gt;backend&lt;/a&gt; repos are public, so feel free to follow along as I add more features.&lt;/p&gt;
&lt;p&gt;Another shoutout goes to Swapnil (&lt;a href=&quot;https://twitter.com/swapnilmmane?s=21&amp;amp;t=fuSkdsKWbtIpZBQwn-mKrg&quot;&gt;@swapnilmmane&lt;/a&gt;) for waking me up by saying &quot;I don’t know what your site is about, it’s a black box&quot; which prompted me to redesign my home page to have more focus on what I do.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2022 Website Rebuild&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A Journey With Dylan: Halfway Through</title><link>https://deliciousreverie.co.uk/posts/a-journey-with-dylan-halfway-through/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/a-journey-with-dylan-halfway-through/</guid><description>A Few weeks ago, I wrote a post about how I was taking a journey through the studio albums of Bob Dylan, starting with his first eponymous album, and discovering new albums and tracks along the way which resonated with me. I&apos;m now about half way through the catalogue, and have encountered a few surprises.</description><pubDate>Tue, 06 Dec 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;A Few weeks ago, I wrote a post about how I was taking a journey through the studio albums of Bob Dylan, starting with his first eponymous album, and discovering new albums and tracks along the way which resonated with me. I&apos;m now about half way through the catalogue, and have encountered a few surprises.&lt;/p&gt;
&lt;p&gt;(&lt;a href=&quot;https://deliciousreverie.co.uk/posts/a-journey-with-dylan/&quot;&gt;Here&apos;s the previous post,&lt;/a&gt; in case you haven&apos;t read it.)&lt;/p&gt;
&lt;p&gt;I wrote a few weeks ago that Dylan is an artist that demands constant re-assessment from his fans. And indeed, some of the assumptions and opinions which I had, and which I&apos;d heard, about his back catalogue have proved ... interesting, if not completely false.&lt;/p&gt;
&lt;h2&gt;1. Dylan&apos;s Early Work is Hard Going &lt;a href=&quot;https://deliciousreverie.co.uk/posts/a-journey-with-dylan-halfway-through/#1.-dylan&apos;s-early-work-is-hard-going&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I had already struggled with Dylan&apos;s earliest work. I can appreciate that he was an artist absorbing, emulating and building on those around him at the time, but I really didn&apos;t identify at all with Bob Dylan, The Times They Are A-Changin&apos; and only just got through Another Side . Maybe I have been tainted a bit by listening to a lot of what came afterwards, but I just can&apos;t see the appeal.&lt;/p&gt;
&lt;p&gt;The Freewheeling&apos; remains my favourite album of this period, and my favourite track of that album has become my favourite duet of all time: &quot;Girl from the North Country&quot; with Johnny Cash is an absolute gem of a record. Cash&apos;s perfectly toned pathos is a great contrast to Dylan&apos;s lighter, more introverted approach.&lt;/p&gt;
&lt;h2&gt;2. Self Portrait Is a Masterpiece &lt;a href=&quot;https://deliciousreverie.co.uk/posts/a-journey-with-dylan-halfway-through/#2.-self-portrait-is-a-masterpiece&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;From what I&apos;d heard, the world was aghast at what Dylan had released in 1970. Even the artist himself claimed that the album was &quot;something of a joke&quot;. So I took the CD out of it&apos;s case ... and didn&apos;t listen to it for a few weeks, fearing the worst.&lt;/p&gt;
&lt;p&gt;Although I&apos;ve discovered a distaste for Dylan covering other artists&apos; work (I hated Dylan — and don&apos;t get me started on the Sinatra albums) there are plenty of absolute diamonds in there. I could listen to &quot;Copper Kettle&quot; and &quot;Early Morning Rain&quot; for hours.&lt;/p&gt;
&lt;p&gt;Taking a rounded out view of the first half of this journey, I&apos;d have to add &quot;Nashville Skyline&quot; as one of my top albums.&lt;/p&gt;
&lt;p&gt;Oh, and listening to John Wesley Harding was going well, until &quot;All Along the Watchtower&quot; started playing, and for the first time I got the context of that song. Whoa.. that changes things. Just having that track amongst some instrumental movie soundtrack music makes it stand out as genius these 50 years later.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A Journey With Dylan: Halfway Through&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A Journey with Dylan</title><link>https://deliciousreverie.co.uk/posts/a-journey-with-dylan/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/a-journey-with-dylan/</guid><description>I&apos;ve been in &amp; out of love with Bob Dylan for many years. I started listening in my early 20&apos;s, bought a compilation album, but I have to say that I just didn&apos;t get it. Now, 16 years later, I&apos;m listening to every album he&apos;s produced so far.</description><pubDate>Fri, 02 Dec 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve been in &amp;amp; out of love with Bob Dylan for many years. I started listening in my early 20&apos;s, bought a compilation album, but I have to say that I just didn&apos;t get it. Now, 16 years later, I&apos;m listening to every album he&apos;s produced so far.&lt;/p&gt;
&lt;p&gt;I guess I might be a glutton for punishment, some of the albums aren&apos;t that easy to listen to. But I want to just chart this journey because Dylan is someone who demands constant reassessment. Every time you think you&apos;ve got the guy sussed, he does something you don&apos;t expect.&lt;/p&gt;
&lt;p&gt;My favourite albums up to this point are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Time Out of Mind&lt;/li&gt;
&lt;li&gt;The Freewheelin&apos;&lt;/li&gt;
&lt;li&gt;Love and Theft&lt;/li&gt;
&lt;li&gt;Blood on the Tracks&lt;/li&gt;
&lt;li&gt;Desire&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Time out of Mind is because that&apos;s the album that got me into Dylan initially; when I heard it I was in a very sultry space and every track suited that atmosphere like it was made for me.&lt;/p&gt;
&lt;p&gt;The Freewheelin&apos; is full of uniqueness and perspective that you just don&apos;t expect from someone that young, especially being his second album.&lt;/p&gt;
&lt;p&gt;Love and Theft was the third album I bought and I really didn&apos;t expect it&apos;s timelessness, antiquity and dry humour. I also love the F Scott Fitzgerald quotes peppered throughout the album.&lt;/p&gt;
&lt;p&gt;Blood on the Tracks. Such raw loss gets my attention every time. This is a very personal album from Dylan, and it&apos;s isolation and pain comes through loud and clear. And then there&apos;s &quot;Lily, Rosemary and the Jack of Hearts&quot; which is a blindside.&lt;/p&gt;
&lt;p&gt;The duets with Emmylou Harris on Desire work so well, but for me this album is more about atmosphere. I love the Southern American feel to what Dylan is doing here. &quot;One More Cup of Coffee&quot; is a great track, even though I&apos;m not a fan of the Spaghetti Western, I can&apos;t help being drawn into that atmosphere listening to this track.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A Journey with Dylan&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Alliteration in Poetry</title><link>https://deliciousreverie.co.uk/posts/alliteration-in-poetry/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/alliteration-in-poetry/</guid><description>I&apos;ve slowly begun to realise something about my taste in poetry, and I wonder how much this is because of my Welsh heritage. As a result, I&apos;m diving more into traditional Welsh poetry, and really enjoying what I find. </description><pubDate>Fri, 15 Mar 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I&apos;ve slowly begun to realise something about my taste in poetry, and I wonder how much this is because of my Welsh heritage. As a result, I&apos;m diving more into traditional Welsh poetry, and really enjoying what I find.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The very fact that poetry is somewhat inaccessible drew me to it. I&apos;m one of those oddballs who revels in the company of unusual people, and who seeks out friendships with people who others find difficult to understand. So poetry was a good candidate for me from the start.&lt;/p&gt;
&lt;p&gt;Due largely to F Scott Fitzgerald (who I got into very young), and another writer I liked (science fiction author Dan Simmons), I got heavily into Keats, and through him John Milton, Byron and to a much lesser extent Tennyson and Wordsworth.&lt;/p&gt;
&lt;p&gt;The descriptions in the works of these poets is delicious, even sumptuous. Keats especially wants us to swoon and even expire because of the beauty of the physical sights around him. And the way he conveys the power of what he sees is amazingly beautiful.&lt;/p&gt;
&lt;p&gt;But I&apos;ve recently discovered other authors that for me at least equal if not surpass Keats in my personal taste. And this is because of the words they write, not the scenes they paint.&lt;/p&gt;
&lt;h2&gt;Philip Nikolayev&lt;/h2&gt;
&lt;p&gt;I have no idea how I stumbled on the Moldovan / Russian / English poet &lt;a href=&quot;/posts/nikolayev-mandelshtam&quot;&gt;Philip Nikolayev&lt;/a&gt;. I know I joined a Facebook group and that was where I first found his translations of Osip Mandelshtam&apos;s own poems from Russian.&lt;/p&gt;
&lt;p&gt;But I loved his keen sense of alliteration. Within each line he places such delightfully similar-sounding words that for me began to exceed even the profound pleasure I got from the deeply meaningful poems I&apos;d read so far.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Insomnia, Homer, taunt sails: my lips have lisped
Down to the middle the detailed list of ships
That long brood and angular train of cranes
That rose above Hellas once on wings of waves.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;In the first line, the alliteration is in the sound at the start of &quot;lips&quot; and &quot;lisped&quot;, and is carried on in the second line&apos;s &quot;list&quot; and &quot;ships&quot;. It&apos;s very playful.&lt;/p&gt;
&lt;p&gt;I don&apos;t know how much of this poem has been written by Nikolayev, but I imagine it&apos;s only the ideas that have come from Osip Mandelshtam: each language has it&apos;s own forms of poetry that are I think unique to that language, and you can&apos;t translate them directly.&lt;/p&gt;
&lt;p&gt;Which is now why I lament that there isn&apos;t more of Raymond Garlick&apos;s poems online that I can share with you.&lt;/p&gt;
&lt;h2&gt;Raymond Garlick and Anglo-Welsh poetry&lt;/h2&gt;
&lt;p&gt;It was quite by chance that my wife gifted me a volume of Raymond Garlick&apos;s poems last year. But during his lifetime last century he was regarded as one of the principal Anglo-Welsh poets.&lt;/p&gt;
&lt;p&gt;That&apos;s a bit of an odd term. Let me explain what is meant by &quot;Anglo-Welsh&quot;. When you hear that expression, it means poetry and literature that&apos;s written in English by a poet that lives in Wales or who has Welsh cultural influences.&lt;/p&gt;
&lt;p&gt;His poems didn&apos;t so much paint pictures of the landscapes of South Wales that he was familiar with as make you think about his personal connection with them. And he used alliteration extensively to convey that.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;To you I give a golden garden, Wales
To play in: half a hundred voels and vales
Shall be your toys, and you shall sing and laugh 
amid a noise of flights of nightingales
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;To me this poem sounds very rich (as in chocolate cake) because of the frequency of alliterations, &quot;golden garden&quot; and &quot;half a hundred&quot;, &quot;Wales&quot;, &quot;voels&quot; and &quot;vales&quot; ... he didn&apos;t happen upon these as if they were coincidences: alliteration is woven in the very fabric of his verse.&lt;/p&gt;
&lt;p&gt;This brings me to the most well known Anglo-Welsh poets: Dylan Thomas. Which is funny, because I used to really hate Dylan Thomas.&lt;/p&gt;
&lt;h2&gt;Dylan Thomas&lt;/h2&gt;
&lt;p&gt;When I was younger and listened to recordings of Dylan Thomas reciting his own poetry, I was completely unimpressed. As well as verging on the nonsensical (this was &quot;Under Milk Wood&quot; instead of the far more poignant &quot;Fern Gully&quot;) it sounded like he was in some sort of trance, as if words were tumbling out of him in what I considered to be a monotonous tone.&lt;/p&gt;
&lt;p&gt;I didn&apos;t appreciate that he was tapping into many hundreds of years of Welsh tradition. And Raymond Garlick was my doorway to understanding how that works.&lt;/p&gt;
&lt;h2&gt;Welsh Medieval Poetry&lt;/h2&gt;
&lt;p&gt;The forms poetry takes is unique to each language it&apos;s spoken in. To translate a poem is to take it&apos;s &lt;em&gt;ideas&lt;/em&gt; and try to convey them in another language; or else try to convey the way it &lt;em&gt;sounds&lt;/em&gt;. One or the other must be sacrificed.&lt;/p&gt;
&lt;p&gt;But let me try to help you understand why that isn&apos;t always necessary:&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Gwae&apos;r gwan dan oedran nid edrych, ni chwardd,
ni cherdda led y rhych 
Gwae ni wyl yn gynilwych
Gwae ni chlyw organ a chlych
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;This is a form of medieval Welsh poetry called &quot;cynghanedd sain&quot;, and to me it seems remarkably similar to the alliteration (a term we use for poetry written in English) of Raymond Garlick&apos;s poems and the cascading rhythym of Dylan Thomas&apos;s as well. The words tumble and fall, trip, skip and scatter in interesting ways across the page.&lt;/p&gt;
&lt;p&gt;I&apos;ve been reading about this effect in an article by Eurys Rowlands in the 4-volume &quot;A guide to welsh literature&quot;. In that essay the author notes that English poetry is created to be more appealing to the eye. You see that in a lot of Romantic poetry: they&apos;re describing a scene or event before them, much like painting with words.&lt;/p&gt;
&lt;p&gt;Welsh poetry is entirely different: it&apos;s written to appeal to the ears. I can&apos;t imagine you&apos;d ever get awful things like trying to rhyme &quot;eye&quot; with &quot;symmetry&quot; just because it looks right. Apologies William Blake but I just can&apos;t forgive that one.&lt;/p&gt;
&lt;p&gt;So you see, I don&apos;t need to translate it for you to be able to enjoy it. As long as you enjoy the way it &lt;em&gt;sounds&lt;/em&gt;, that is the real power of the verse. It&apos;s meaning can be an enhancement.&lt;/p&gt;
&lt;p&gt;And for me, that means I have found an interesting way for me to learn the language, and that&apos;s something I&apos;ve really struggled with over the past few months: I don&apos;t easily succumb to things like &quot;gamification&quot; so Duolingo isn&apos;t a good fit, and I don&apos;t have time for a regular scheduled course.&lt;/p&gt;
&lt;p&gt;However, I do have time to translate the odd bit of poetry. I make the time, because it&apos;s something I really enjoy.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Alliteration in Poetry&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Alternatives to Heroku’s Free Tier</title><link>https://deliciousreverie.co.uk/posts/alternatives-herokus-free-tier/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/alternatives-herokus-free-tier/</guid><description>Originally written for the Webiny blog. Heroku announced that it&apos;s extremely popular free tier plans were being phased out and I saw an opportunity to provide some high value content that could capture the audience and attract them to Webiny as a product.</description><pubDate>Fri, 26 Aug 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Heroku announced yesterday that it&apos;s extremely popular free tier plans are being phased out starting on November 28 this year (2022). It&apos;s the end of an era for a lot of people, particularly independent bloggers who have hobby sites, developers evaluating software for use at their organizations, and non-profits who host their Headless CMS backend. But is there an alternative to Heroku?&lt;/p&gt;
&lt;h2&gt;The Heroku Story&lt;/h2&gt;
&lt;p&gt;Heroku started as a platform offering hosting to exclusively Ruby-based websites. It&apos;s since expanded that option and now a host of &quot;build packs&quot; for other languages exist which can deploy infrastructure to Heroku in one click. This was likely the inspiration for similar &quot;one click deploy&quot; buttons that have been adopted by Netlify, Vercel and others.&lt;/p&gt;
&lt;p&gt;The unique thing about the way Heroku was built is that it leveraged &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Hibernate.html&quot;&gt;AWS&apos; ability to hibernate EC2 instances&lt;/a&gt;. So when you deploy your infrastructure to one of the free-tier &quot;dynos&quot; (a dyno is an environment in which your application can run, similar to containers), after a while of inactivity the dyno would hibernate: it would turn off, or go to sleep. The effect of this was that you couldn&apos;t contact the application straight away in that state. It would take around 30 seconds for it to activate, or wake up. This was enough time for Heroku to spin up your dyno again so that it can start accepting requests.&lt;/p&gt;
&lt;p&gt;This method did come with some complications like ephermeral file storage, meaning you couldn&apos;t store things like SQLLite databases or upload images alongside your application. But there were other Heroku services and 3rd party integrations to allow you to use databases and store images and other files.&lt;/p&gt;
&lt;p&gt;This was a great help to many people who&apos;s main concern was with small blogs built on an open source blogging platform such as Ghost or Strapi. With Heroku you could deploy your application and build a separate, static frontend (which would be hosted on a separate service, such as &lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt; or &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;). That way, you could write an article, rebuild your static site, and afterwards allow the application to hibernate. And it would be free to use.&lt;/p&gt;
&lt;h2&gt;Alternatives to Heroku&lt;/h2&gt;
&lt;p&gt;Looking for an alternative to Heroku depends on what you were using for. Many people had used it to host backend services for small hobby apps that didn&apos;t make any money, or for proof-of-concept ideas so they could evaluate tools for their organizations to later use in production.&lt;/p&gt;
&lt;p&gt;If this is what you&apos;re looking for, there are a plethora of services which deploy containerized applications to a managed environment and that offer free tiers with different pricing models depending on what databases persistent storage and other services you require.&lt;/p&gt;
&lt;p&gt;Depending on what you were using, it&apos;s feasible that you could move your application stack there without too many code changes. In fact, both Fly and Render offer guides for migrating from Heroku:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Speed up a Heroku App with Fly.io&lt;/li&gt;
&lt;li&gt;Migrate from Heroku to Render&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But some other popular alternatives include&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linode Managed Hosting&lt;/li&gt;
&lt;li&gt;Digital Ocean Managed Cloud Hosting&lt;/li&gt;
&lt;li&gt;Firebase Build&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You could also get quite far by using the serverless functions that now come with (formerly) static hosting providers like &lt;a href=&quot;https://vercel.com/docs/concepts/functions/serverless-functions&quot;&gt;Vercel&lt;/a&gt;, &lt;a href=&quot;https://www.netlify.com/products/functions/&quot;&gt;Netlify&lt;/a&gt;, and &lt;a href=&quot;https://www.gatsbyjs.com/products/cloud/hosting&quot;&gt;Gatsby Cloud&lt;/a&gt;. There are even complex frameworks like &lt;a href=&quot;https://redwoodjs.com/&quot;&gt;RedwoodJs&lt;/a&gt; that have been built on top of these providers.&lt;/p&gt;
&lt;p&gt;If you were using Heroku for a Headless CMS for a static frontend, most of the open-source Headless CMS platforms offer their own cloud-based infrastructure which will avoid having to re-platform to a different CMS altogether.&lt;/p&gt;
&lt;p&gt;There is however potentially another problem looming...&lt;/p&gt;
&lt;h2&gt;Stay in Control of Your Application Stack&lt;/h2&gt;
&lt;p&gt;If Heroku can suddenly turn around and kill it&apos;s free-for-starters offering, who&apos;s to say it won&apos;t happen for these other services? Even Vercel and Netlify leverage the raw computing power of cloud providers such as AWS, Azure and Google Cloud, repackaging those services and selling them at a profitable margin.&lt;/p&gt;
&lt;p&gt;For that reason, we think the best alternative is to embrace these foundational computing platforms, instead of standing again on the unsteady ground of a reseller.&lt;/p&gt;
&lt;p&gt;AWS and other providers have a &lt;a href=&quot;https://aws.amazon.com/free/?all-free-tier.sort-by=item.additionalFields.SortRank&amp;amp;all-free-tier.sort-order=asc&amp;amp;awsf.Free%20Tier%20Types=*all&amp;amp;awsf.Free%20Tier%20Categories=*all&quot;&gt;free tier&lt;/a&gt; which applies to most of its services. These are specific allowances for each of their services that you can use before you start incurring fees.&lt;/p&gt;
&lt;p&gt;With this and similar offerings from other cloud providers like Google Cloud Platform and Microsoft Azure, you could set up an EC2 instance (similar to a virtual machine), configure it to hibernate in a similar fashion to what Heroku was doing, and move your application to it manually.&lt;/p&gt;
&lt;p&gt;This would take a bit of work, either &lt;a href=&quot;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AccessingInstancesLinux.html&quot;&gt;to SSH in and upload your application&lt;/a&gt;, or &lt;a href=&quot;https://aws.amazon.com/getting-started/hands-on/set-up-ci-cd-pipeline/&quot;&gt;set up a deployment pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But there&apos;s also another opportunity here that we would like to highlight.&lt;/p&gt;
&lt;h2&gt;Embrace Serverless&lt;/h2&gt;
&lt;p&gt;If you notice, the limits on EC2 instances for containerized applications are time-based. You get a certain number of free hours in traditional &quot;always-on&quot; computing models. After that you need to pay for them. Even though this is quite a generous offering, you could still leave yourself with a substantial bill if you&apos;re not careful.&lt;/p&gt;
&lt;p&gt;However, other services are based on the number of invocations. Why not take advantage of these?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functions as a service with AWS Lambda: 1 million free requests per month&lt;/li&gt;
&lt;li&gt;File storage You get 5GB of S3 static file storage (think images and static website hosting)&lt;/li&gt;
&lt;li&gt;AWS&apos; no-sql Database, DynamoDB, comes at 25GB of storage for free&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The next time you need to build a proof of concept, or hobby app, you could build your solution using a combination of these and host it indefinitely. It wouldn&apos;t need special configuration to hibernate since that is it&apos;s natural state.&lt;/p&gt;
&lt;p&gt;Why not try serverless in your next proof of concept or hobby project?&lt;/p&gt;
&lt;p&gt;To do so, you could leverage a framework which allows you to write infrastructure-as-code, such as &lt;a href=&quot;https://aws.amazon.com/amplify/&quot;&gt;AWS Amplify&lt;/a&gt;, &lt;a href=&quot;https://sst.dev/&quot;&gt;Serverless Stack&lt;/a&gt;, the &lt;a href=&quot;https://www.serverless.com/&quot;&gt;Serverless framework&lt;/a&gt;, or &lt;a href=&quot;https://www.pulumi.com/&quot;&gt;Pulumi&lt;/a&gt;. There is a bit of an overhead to learning these tools so you will need to research which one is best for your needs.&lt;/p&gt;
&lt;p&gt;But there&apos;s an alternative...&lt;/p&gt;
&lt;h2&gt;Webiny: Leverage the Power of Serverless&lt;/h2&gt;
&lt;p&gt;For a little over two years, we have been building Webiny. It&apos;s a Headless CMS you can use for your personal blog ... but it&apos;s not only a Headless CMS: it&apos;s a fully-featured application framework that you can use to scaffold unique applications that will have access to the existing setup we have built via plugins.&lt;/p&gt;
&lt;p&gt;You don&apos;t need to reinvent the wheel of how to store files, or how to build a GraphQL API, or how to build an admin interface because we&apos;ve already done that for you.&lt;/p&gt;
&lt;p&gt;And what is more, you get all of that for free when you choose the DynamoDB-only option in your setup. You will eventually pay for the amount of requests, storage and database usage ... but only if you exceed the free tier on AWS.&lt;/p&gt;
&lt;p&gt;Once your proof-of-concept has satisfied it&apos;s requirements, you can destroy your Webiny instance ... or leave it there for future tinkering!&lt;/p&gt;
&lt;p&gt;And if you want a place to write blog articles, &lt;a href=&quot;https://www.webiny.com/enterprise-serverless-cms/headless-cms&quot;&gt;Webiny is built to do that out-of-the-box&lt;/a&gt;.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Alternatives to Heroku’s Free Tier&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Automated accessibility testing is great, but ...</title><link>https://deliciousreverie.co.uk/posts/automated-accessibility-testing-is-great-but/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/automated-accessibility-testing-is-great-but/</guid><description>We recently had a situation using jest-axe in tests, where the landmarks feature wasn&apos;t work as expected. This helped me verify why we shouldn&apos;t rely solely on automated tools, never mind how good they are. </description><pubDate>Wed, 20 Jan 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;We recently had a situation using jest-axe in tests, where the landmarks feature wasn&apos;t work as expected. This helped me verify why we shouldn&apos;t rely solely on automated tools, never mind how good they are.&lt;/p&gt;
&lt;p&gt;In unit tests for our &lt;a href=&quot;https://github.com/zopaUK/react-components&quot;&gt;react component library&lt;/a&gt; at Zopa, we have utilised &lt;a href=&quot;https://github.com/nickcolley/jest-axe&quot;&gt;jest-axe&lt;/a&gt; to test whether our components adhere to a number of accessibility rules.&lt;/p&gt;
&lt;p&gt;The funny thing was, when we adhered to the rules (specifically the way components either use semantic HTML or aria roles), further testing in &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse/&quot;&gt;Google&apos;s Lighthouse tool&lt;/a&gt; revealed that there were accessibility issues with our components:&lt;/p&gt;
&lt;p&gt;Some ARIA child roles must be contained by specific parent roles to properly perform their intended accessibility functions.&lt;/p&gt;
&lt;p&gt;The trouble is that our library contains a lot of components that do not require the use of landmarks. We build our UI pages up from these component pieces, and would utilise landmarks on the page typically.&lt;/p&gt;
&lt;p&gt;This rule was forcing our developers to add aria roles where they weren&apos;t needed.&lt;/p&gt;
&lt;p&gt;Thankfully, &lt;a href=&quot;https://github.com/nickcolley/jest-axe/issues/92&quot;&gt;You can disable this rule&lt;/a&gt;. However, this means that there are far less checks for semantic HTML in our codebase.&lt;/p&gt;
&lt;p&gt;I don&apos;t think this kind of issue is something that&apos;s easily solved by the axe team. It does highlight the flaws in tools like jest-axe: we can&apos;t rely solely on them. In fact, right on the Github repo the team identify clearly (in large writing!) that &quot; This project does not guarantee what you build is accessible&quot;, and that &quot;The GDS Accessibility team found that only ~30% of issues are found by automated testing.&quot;&lt;/p&gt;
&lt;p&gt;We ultimately decided that ~30% is better than nothing. And &amp;lt;div&amp;gt; soup, whilst undesirable, is better than using roles where they&apos;re misleading. So we disabled this rule and we&apos;re going to continue to use jest-axe in our project.&lt;/p&gt;
&lt;h2&gt;Other ways forward &lt;a href=&quot;https://deliciousreverie.co.uk/posts/automated-accessibility-testing-is-great-but/#other-ways-forward&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To try to keep our focus on giving all of our users the best experience possible, we have recently created the role of &quot;feature champions&quot; among the frontend community.&lt;/p&gt;
&lt;p&gt;These individuals take on a subject that they are passionate about, such as TypeScript, unit testing and, yes, accessibility too, and will occasionally give a quick eye to projects to kindly advise whether we are following best practices.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Automated accessibility testing is great, but ...&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Book Review: The City &amp; The City, China Mieville</title><link>https://deliciousreverie.co.uk/posts/book-review-the-city-and-the-city-china-mieville/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/book-review-the-city-and-the-city-china-mieville/</guid><description>Time is precious for me having a kid and a demanding job, so I&apos;m pleased I&apos;ve managed to find the time to read The City &amp; The City, China Mieville&apos;s (very successful) attempt at the crime thriller genre. </description><pubDate>Wed, 15 Jun 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Time is precious for me having a kid and a demanding job, so I&apos;m pleased I&apos;ve managed to find the time to read The City &amp;amp; The City, China Mieville&apos;s (very successful) attempt at the crime thriller genre.&lt;/p&gt;
&lt;p&gt;I&apos;m not typically a reader of crime fiction, however I&apos;ve really enjoyed some of Mieville&apos;s urban fantasy work in the past, and know he&apos;s a smart, well-researched writer who is prone to a bit of genre-hopping ... his plots often take unexpected twists, for which this work is no exception.&lt;/p&gt;
&lt;h3&gt;The City &lt;a href=&quot;https://deliciousreverie.co.uk/posts/book-review-the-city-and-the-city-china-mieville/#the-city&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Melville, writing as he often does from the first person perspective, describes an inspector sent to investigate a grimy murder in Beszel, a city &quot;on the outskirts of Europe&quot;, which we hear is jostling for economic position with it&apos;s more successful neighbour, Ul Quoma.&lt;/p&gt;
&lt;p&gt;But it&apos;s pretty soon we realise that this other city isn&apos;t in a separate location from Beszel, but in the same place. It&apos;s quite difficult to describe, but you get parts of the topography which are located in Beszel, but some are in Ul Quoma. So you could live &quot;gross topically&quot; (a word coined to describe a physical location which nevertheless is in a different political locality — that of the other city) next to someone who lives in a totally different city. A city for which, if you wanted to visit them, you needed to go to a consulate and obtain a visa. This is complicated by parts of the territory that are &quot;crosshatched&quot;, or located in both cities, but through which you have to navigate residents of the other cities (&quot;foreigners&quot; to you) without interacting, or even noticing them.&lt;/p&gt;
&lt;p&gt;Make sense? No? Stop there, go get the book, because it does get weirder.&lt;/p&gt;
&lt;h3&gt;Borders &lt;a href=&quot;https://deliciousreverie.co.uk/posts/book-review-the-city-and-the-city-china-mieville/#borders&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The borders are policed by a seemingly omnipotent force which is above the law called Breach. If you, even inadvertently, step over in to the other city, or speak to a &quot;foreigner&quot;, they will immediately arrest you. You might not ever come back once they do.&lt;/p&gt;
&lt;p&gt;When our detective finds out that the murder may have been committed in Ul Quoma, the foreign city, he needs to cross over to uncover what happened to her, and in the process discovers that something more sinister is happening.&lt;/p&gt;
&lt;h3&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/book-review-the-city-and-the-city-china-mieville/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I really enjoyed this book, but always feared it would step too far away from the crime genre to be accessible. However, it doesn&apos;t do that. Even though the plot takes some pretty intense turns that leave you reeling, its still a crime thriller through and through.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Book Review: The City &amp;amp; The City, China Mieville&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Atomic Development</title><link>https://deliciousreverie.co.uk/posts/atomic-development/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/atomic-development/</guid><description>Structuring components in a reusable, discoverable way has been a challenge for some recent projects I&apos;ve worked on. I&apos;ve seen different approaches used, but now I think I&apos;ve found a method that&apos;s basically intuitive, avoids duplication of effort and facilitates easy discovery of components.</description><pubDate>Thu, 04 Oct 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Structuring components in a reusable, discoverable way has been a challenge for some recent projects I&apos;ve worked on. I&apos;ve seen different approaches used, but now I think I&apos;ve found a method that&apos;s basically intuitive, avoids duplication of effort and facilitates easy discovery of components.&lt;/p&gt;
&lt;p&gt;I didn&apos;t come up with this approach. &lt;a href=&quot;https://bradfrost.com/blog/post/atomic-web-design/&quot;&gt;Brad Frost published the book &quot;Atomic Design&quot; 2 years ago&lt;/a&gt;, and I was fascinated with his idea, that you break down your components to the smallest reusable unit, then build them up again piece by piece. Just as living organisms are made up of atoms, then molecules, then larger entities until you get the complete creature.&lt;/p&gt;
&lt;p&gt;I tried this approach on a recent React project, and want to outline some of it&apos;s benefits. It&apos;s important to note that I don&apos;t believe it&apos;s 100% the best approach, since I don&apos;t think there is one. What I do want to show is where it&apos;s useful, where it isn&apos;t, and hopefully what those limitations are.&lt;/p&gt;
&lt;h2&gt;The Atomic Design Approach &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#the-atomic-design-approach&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Brad Frost&apos;s book outlined a way to structure the design of a project based on building things up from the smallest component part.&lt;/p&gt;
&lt;p&gt;For example, this could be an input field. Input fields are never used on their own, so the next item up the scale would be an input field and a label. This would be your atom and molecule respectively.&lt;/p&gt;
&lt;p&gt;You could keep building upwards in this way so that you eventually had a fully designed page using a template which contained your complete form (an organism) which is made up of your separately designed molecules.&lt;/p&gt;
&lt;p&gt;This approach could help designers think more about the patterns they are building into a project, instead of treating every item as separate compositions, which would then require a lot more work from the development team and time and money from the project owner.&lt;/p&gt;
&lt;p&gt;But I believe we can utilise this approach in development so that there&apos;s more cross-functional collaboration between design and development. And it can also help developers structure their projects well.&lt;/p&gt;
&lt;h2&gt;Atomic Development &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#atomic-development&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a recent project I decided to investigate whether this approach could be uilised in development. In my React project I typically use Styled Components to write my CSS styles. I therefore structured my project like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- components/
  - Atoms
  - Molecules/
  - Organisms/
  - Templates/
  - Pages/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Atoms &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#atoms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Atoms I designated as constants which could receive props. For example, a paragraph that had a prop for the text colour.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const Paragraph = styled.p`
    ${props =&amp;gt;
      props.textColor
        ? `color: ${props.textColor};`
        : null }
  font-size: ${variable.REGULAR};
  margin-bottom: ${variable.SINGLE};
  &amp;amp; small {
    color: ${variable.BRAND_HILIGHT};
  }
  &amp;gt; a {
    ${props =&amp;gt;
      props.textColor
        ? `color: ${props.textColor};`
        : null }
    text-decoration: underline;
    &amp;amp;:hover,
    &amp;amp;:active,
    &amp;amp;:focus {
      color: ${variable.BRAND_HILIGHT};
    }
  }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I kept all of these in a single file because I initially believe there wouldn&apos;t be too many small components, and they were so tiny they didn&apos;t justify having a separate file for each.&lt;/p&gt;
&lt;p&gt;I now believe that approach wasn&apos;t the best one. As I got further involved in development, and I had built up a larger library of components, I struggled to remember what I had designated as an Atom, and what was a Molecule. In future, I&apos;m going to separate these components into individual files to facilitate easier discovery.&lt;/p&gt;
&lt;h3&gt;Molecules &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#molecules&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Molecules consist of stateless functional components, often comprising of several Atomic elements and usually some custom Atoms that relate to that specific Molecule.&lt;/p&gt;
&lt;p&gt;This is a &quot;popout&quot; section, which has a custom MainContainer element that&apos;s only used in this Molecule. It can accept multiple props and can wrap other components.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const MainContainer = styled.div`
  width: 100%;
  background-color: ${props =&amp;gt; props.backgroundColor ? props.backgroundColor : variable.BRAND_PRIMARY };
  height: 100%;
  padding: ${variable.SINGLEplusHALF};
  text-align:center;
  color: ${props =&amp;gt; props.textColor ? props.textColor : variable.BRAND_HILIGHT };
  border-radius:${props =&amp;gt; props.corner ? props.corner : variable.RADIUS_CORNER };
  position: relative;
  background-size:cover;
  background-position: 50% 50%;

  @media (min-width: ${variable.BREAK_TABLET}) {
    padding: ${props =&amp;gt; props.NarrowView ? `${variable.QUAD} ${variable.SEXTUPLE};` : `${variable.QUAD};` };
  }
`


const PopOut = props =&amp;gt; (
  &amp;lt;Container&amp;gt;
    &amp;lt;MainContainer {...props} style={ props.background ? {backgroundImage : `url(${props.background})`}: null } &amp;gt;
    {props.children}
    &amp;lt;/MainContainer&amp;gt;
  &amp;lt;/Container&amp;gt;
)

export default PopOut
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Organisms &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#organisms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As we get to organisms, we start to see Classes being used, bringing in several Molecules and atoms, often with components that render on the frontend as well as server.&lt;/p&gt;
&lt;p&gt;This is a carousel component. It had several smaller components and so many unique styles that they were collocated within a folder.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class CardsCarousel extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      &amp;lt;Main
        style={
          this.props.backgroundimg
            ? {
                backgroundImage: `url(${this.props.backgroundimg})`,
                backgroundSize: `cover`,
              }
            : null
        }
      &amp;gt;
        &amp;lt;Container {...this.props}&amp;gt;
          &amp;lt;MainTextContainer&amp;gt;
            &amp;lt;HeadingSecondary textColor={this.props.textColor}&amp;gt;
              {this.props.heading || &apos;Recent News&apos;}
            &amp;lt;/HeadingSecondary&amp;gt;
            &amp;lt;Paragraph textColor={this.props.textColor}&amp;gt;
              {this.props.content || &apos;Content.&apos;}
            &amp;lt;/Paragraph&amp;gt;
            &amp;lt;Button to=&quot;/news/&quot; white&amp;gt;
              Read more articles
            &amp;lt;/Button&amp;gt;
          &amp;lt;/MainTextContainer&amp;gt;
          &amp;lt;CardsContainer&amp;gt;
              &amp;lt;Carousel lightBubbles {...CAROUSEL_SETTINGS_LARGE}&amp;gt;
                {this.props.posts
                  ? this.props.posts.map(item =&amp;gt; (
                      &amp;lt;Card key={item.node.id} data={item.node} /&amp;gt;
                    ))
                  : null}
              &amp;lt;/Carousel&amp;gt;
          &amp;lt;/CardsContainer&amp;gt;
        &amp;lt;/Container&amp;gt;
      &amp;lt;/Main&amp;gt;
    )
  }
}

export default CardsCarousel
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Partials &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#partials&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I developed this project, I realised there was an extra item which I didn&apos;t feel fitted into either Organisms or Templates. These were the site&apos;s headers, footer, and a parent component called Layout which would wrap all of the pages and provide some base CSS (fonts and a minimal reset).&lt;/p&gt;
&lt;p&gt;So I created a Partials folder for these 3 components. I&apos;m still not certain they shouldn&apos;t have been Organisms. But easy discovery for me meant that I wanted to keep them separate from other, re-usable Organisms that would be used in different contexts.&lt;/p&gt;
&lt;h3&gt;Templates &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#templates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Does what it says on the tin really. These are where the layout for pages that were programmatically created from other data sources are kept.&lt;/p&gt;
&lt;h3&gt;Pages &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#pages&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Fully formed JSX pages lived here, comprising of a component that would wrap every page (Partials/Layout.js), and many Organisms, Molecules and Atoms to build up entire pages.&lt;/p&gt;
&lt;p&gt;Here&apos;s a short example of a page so you can see the final use of this approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;
import Helmet from &apos;react-helmet&apos;
import * as variable from &apos;../components/variables&apos;
import Layout from &apos;../components/Templates/Layout&apos;
import Link from &apos;../components/Molecules/Link&apos;
import * as atom from &apos;../components/Atoms&apos;

import Masthead from &apos;../components/Organisms/Masthead&apos;

export const frontmatter = {
  ---
title: &quot;NOT FOUND&quot;,
  path: &quot;404&quot;,
  description: &quot;Page not found&quot;,
  inMainNav: false
}

const NotFoundPage = props =&amp;gt; (
  &amp;lt;Layout&amp;gt;
    &amp;lt;Helmet&amp;gt;
      &amp;lt;title&amp;gt;NOT FOUND | { props.data.site.siteMetadata.title }&amp;lt;/title&amp;gt;
      &amp;lt;meta name=&quot;description&quot; content=&quot;Page not found.&quot; /&amp;gt;
    &amp;lt;/Helmet&amp;gt;
    &amp;lt;Masthead
        title=&quot;NOT FOUND&quot;
        intro=&quot;Page not found&quot;
        textColor={variable.BRAND_SECONDARY}
    /&amp;gt;
    &amp;lt;atom.Band&amp;gt;
      &amp;lt;atom.Container&amp;gt;
        &amp;lt;atom.Paragraph&amp;gt;You just hit a route that doesn&amp;amp;#39;t exist... the sadness.&amp;lt;/atom.Paragraph&amp;gt;
        &amp;lt;atom.Paragraph&amp;gt;You might want to check out the &amp;lt;Link to=&quot;/sitemap&quot;&amp;gt;sitemap&amp;lt;/Link&amp;gt;, or go back to the &amp;lt;Link to=&quot;/&quot;&amp;gt; homepage&amp;lt;/Link&amp;gt;.&amp;lt;/atom.Paragraph&amp;gt;
      &amp;lt;/atom.Container&amp;gt;
    &amp;lt;/atom.Band&amp;gt;
  &amp;lt;/Layout&amp;gt;
)

export default NotFoundPage

export const query = graphql`
query notFoundPageQuery {
  site {
    siteMetadata {
      title
    }
  }
}
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see from this code, an approach I started to adopt included importing everything from atoms and using &lt;code&gt;&amp;lt;atom.band&amp;gt;&lt;/code&gt; to identify clearly what grouping it came under. It also made imports much shorter and somewhat tidier.&lt;/p&gt;
&lt;h3&gt;Atomic Could Be the Future &lt;a href=&quot;https://deliciousreverie.co.uk/posts/atomic-web-development/#atomic-could-be-the-future&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The title of this section is a bit of a misnomer. The Atomic approach might not be best for your project. It might not fit your development team workflow. Or it might not match your particular mental model of how you see your project&apos;s code. That&apos;s OK, I document this in the hopes that it might be useful to some.&lt;/p&gt;
&lt;p&gt;If you do end up adopting this approach I&apos;d really like to know about it.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Atomic Development&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Better Type Safety with JSDoc</title><link>https://deliciousreverie.co.uk/posts/better-type-safety-with-jsdoc/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/better-type-safety-with-jsdoc/</guid><description>This is a follow-up article to one I wrote a few months ago, and which quickly became the most popular article on my blog. I&apos;ve learned a few more tricks, including how to ensure that types in events are respected.</description><pubDate>Thu, 18 Jul 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;This is a follow-up article to one I wrote a few months ago, and which quickly became the most popular article on my blog. I&apos;ve learned a few more tricks, including how to ensure that types in events are respected.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/posts/types-via-jsdoc-or-typescript&quot;&gt;Here&apos;s a link to the first article&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One thing I struggled with when I started using JSDoc was how to type event handlers without TypeScript.&lt;/p&gt;
&lt;p&gt;Take a look at the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const button = document.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&quot;button#my-button&quot;);

button.addEventListener(&quot;click&quot;, (event) =&amp;gt; {
  const toggleThis = event.target.closest(&quot;.toggle-container&quot;); // TypeError: closest does not exist on type EventTarget
  sendToApi(JSON.stringify(event.detail)); // TypeError: detail does not exist on Element
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With TypeScript, you can type the selector or event directly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const button = document.querySelector&amp;lt;HTMLButtonElement&amp;gt;(&quot;button#my-button&quot;);

button.addEventListener(&quot;click&quot;, ((
  // you don&apos;t need both of these, this is just an example
  event: MouseEvent
  ) =&amp;gt; {
  const toggleThis = event.target.closest(&quot;.toggle-container&quot;);
  sendToApi(JSON.stringify(event.detail));
}));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have to think a little bit differently with JSDoc. However, we can have &lt;em&gt;more&lt;/em&gt; type safety with JSDoc:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const button = document.querySelector(&quot;button#my-button&quot;);

if(!button || button instanceof HMTLButtonElement) {
  return;
}

button.addEventListener(&quot;click&quot;, (event) =&amp;gt; {
  const toggleThis = event.target.closest(&quot;.toggle-container&quot;);
  sendToApi(JSON.stringify(event.detail));
})

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why do I say &lt;em&gt;more&lt;/em&gt; type safety?&lt;/p&gt;
&lt;p&gt;Because TypeScript is compiled away at run time. It doesn&apos;t make it into code that runs in a browser, because it&apos;s not valid JavaScript.&lt;/p&gt;
&lt;p&gt;What would happen if the button wasn&apos;t in the DOM? Where&apos;s your static analysis now?&lt;/p&gt;
&lt;p&gt;This is something that&apos;s very easy to miss if you&apos;re writing TypeScript because (a) you can easily &lt;em&gt;force&lt;/em&gt; the compiler to recognise the button is there (via a dirty &lt;code&gt;!&lt;/code&gt; after the selector), resulting in (b) a possible error in production if the button isn&apos;t there for some reason (for example, some code that produces it has failed, some developer in another squad removed it without telling you, or somebody replaced it with a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It could be relatively easy to get into one of these situations ... JSDoc ensures that type safety is baked into your &lt;em&gt;production&lt;/em&gt; environments, not just your development code.&lt;/p&gt;
&lt;p&gt;I&apos;ve been using mostly JSDoc for the last 18 months, and I&apos;m definitely preferring it to TypeScript. I would lean on TS for React still but other than that it&apos;s very robust. It&apos;s really sad that there isn&apos;t more documentation for it, and I think that causes a lot of people to steer towards TypeScript.&lt;/p&gt;
&lt;p&gt;But if you have the luxury to choose, definitely consider using JSDoc.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Better Type Safety with JSDoc&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Borges and I</title><link>https://deliciousreverie.co.uk/posts/borges-and-i/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/borges-and-i/</guid><description>As a teenager, and on into my twenties, I developed a real affinity for existential philosophy, and really enjoyed reading the works of Kafka, Alain Robbe-Gruillet, Satre, Camus, Samuel Beckett and others. Though these writings aren&apos;t what you might call easy reading, they appealed to this somewhat narcissistic, isolated youth who already felt as if he didn&apos;t really belong anywhere.</description><pubDate>Sat, 30 Sep 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;As a teenager, and on into my twenties, I developed a real affinity for existential philosophy, and really enjoyed reading the works of Kafka, Alain Robbe-Gruillet, Satre, Camus, Samuel Beckett and others. Though these writings aren&apos;t what you might call &quot;easy reading&quot;, they appealed to this somewhat narcissistic, isolated youth who already felt as if he didn&apos;t really belong anywhere.&lt;/p&gt;
&lt;p&gt;One author that I never encountered as part of that group was the Argentinian writer and translator Jorge Luis Borges, and it wasn&apos;t until I stumbled across a collection of his work that I even heard of him.&lt;/p&gt;
&lt;h2&gt;Borges the Translator &lt;a href=&quot;https://deliciousreverie.co.uk/posts/borges-and-i/#borges-the-translator&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Borges was a translator into Argentinian of popular Western works. I get the sense that this work must not have challenged him very much, since he didn&apos;t exactly stick to the material he had been given. I am told that he sometimes introduces new text, plot lines and even entirely new characters.&lt;/p&gt;
&lt;p&gt;I know what you&apos;re thinking, &quot;that is a betrayal of what it means to translate&quot; ... and perhaps you would be right.&lt;/p&gt;
&lt;p&gt;But perhaps there&apos;s another view.&lt;/p&gt;
&lt;p&gt;I&apos;ve often thought that the work of a translator is an act of co-creation, not merely one of replacing one word for another. What got me thinking along these lines is when I heard that people signing for the deaf are called &quot;interpreters&quot;, not &quot;translators&quot;, since they are interpreting words into something else - thoughts or phrases that can be understood in a different medium.&lt;/p&gt;
&lt;p&gt;Was Borges trying to force this point? Or was he just being facetious, taking liberties because he knew his audience would be none the wiser. I guess we may never know.&lt;/p&gt;
&lt;h2&gt;Borges the Writer &lt;a href=&quot;https://deliciousreverie.co.uk/posts/borges-and-i/#borges-the-writer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When he came into his own as a writer, Borges&apos; tales can verge on the kind of creepiness HP Lovecraft fans would adore; tales of vast, hidden cultures with special knowledge in dense jungle lands, of unintelligible worlds existing just beyond our own.&lt;/p&gt;
&lt;p&gt;As an example of this type of story, &quot;The Garden of Forking Paths&quot;, takes a somewhat paranoid narrator trying to escape a determined killer by finding some hidden garden through which he can escape, only to find that the killer knows the maze better than he knows it himself, and so the narrator cannot escape the inevitable.&lt;/p&gt;
&lt;p&gt;But what I really love is Borges&apos; ability to get me to question my own world view: more often than not, his mostly short, single page or two-chapter stories end with a plot twist sure to cut you to the quick; quite often playing tricks with the role of narrator to spin your interpretation of the story completely on its heels.&lt;/p&gt;
&lt;p&gt;My absolute favourite of these stories is &quot;Borges and I&quot;, the last story in the compendium I own. I&apos;ll reproduce the entire delicious text here by way of a conclusion.&lt;/p&gt;
&lt;p&gt;I argue that Borges should not so readily be ignored, and be much less eagerly forgotten as he seems to be today. Instead I think he should be considered as among the core group of existentialist writers that I mentioned at the start of this article. I&apos;d love to see more people recognise this talented, mischevious and thought-provoking artist for what he was, a true original among his contemporary 20th century authors.&lt;/p&gt;
&lt;h2&gt;Borges and I &lt;a href=&quot;https://deliciousreverie.co.uk/posts/borges-and-i/#borges-and-i&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s to that other one, to Borges, that things happen. I walk through Buenos Aires and I pause, one could say mechanically, to gaze at a vestibule’s arch and its inner door; of Borges I receive news in the mail and I see his name in a list of professors or in some biographical dictionary. I like hourglasses, maps, eighteenth-century typefaces, etymologies, the taste of coffee and the prose of Stevenson; the other shares these preferences, but in a vain kind of way that turns them into an actor’s attributes. It would be an exaggeration to claim that our relationship is hostile; I live, I let myself live so that Borges may write his literature, and this literature justifies me. It poses no great difficulty for me to admit that he has put together some decent passages, yet these passages cannot save me, perhaps because whatsoever is good does not belong to anyone, not even to the other, but to language and tradition. In any case, I am destined to lose all that I am, definitively, and only fleeting moments of myself will be able to live on in the other. Little by little, I continue ceding to him everything, even though I am aware of his perverse tendency to falsify and magnify.&lt;/p&gt;
&lt;p&gt;Spinoza understood that all things strive to persevere being; the stone wishes to be eternally a stone and the tiger a tiger. I will endure in Borges, not in myself (if it is that I am someone), but I recognise myself less in his books than in those of many others, or in the well-worn strum of a guitar. Years ago I tried to free myself from him by moving on from the mythologies of the slums to games with time and infinity, but those games are now Borges’ and I will have to conceive of other things. Thus my life is a running away and I lose everything and everything is turned over to oblivion, or to the other.&lt;/p&gt;
&lt;p&gt;I do not know which of the two is writing this piece.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Borges and I&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Both Sides, Now</title><link>https://deliciousreverie.co.uk/posts/both-sides-now/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/both-sides-now/</guid><description>Many people I respect and admire have voiced conflicting opinions on their blogs and Twitter recently. I wanted to reply in general with reference to a song that pops into my head every time I hear those conflicting opinions being discussed. </description><pubDate>Tue, 12 Mar 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;CSS isn&apos;t a programming language. CSS is a programming language. JavaScript shouldn&apos;t be used to render HTML. JavaScript is the only way of writing HTML. CSS is overly complicated, difficult to learn and hopelessly outdated. All of these opinions provoke a response in me. It&apos;s just perhaps not the one the authors intended.&lt;/p&gt;
&lt;p&gt;When I see comments like the ones above I can&apos;t help but wonder if they&apos;re written out of a genuine frustration or concern a person has, whether they&apos;re meant to provoke an argument, or even whether they are using others&apos; frustration to raise their own profile.&lt;/p&gt;
&lt;p&gt;Whatever the case, there&apos;s another way of looking at the situation that I hope others can benefit from:&lt;/p&gt;
&lt;h1&gt;Both Sides, Now &lt;a href=&quot;https://deliciousreverie.co.uk/posts/both-sides-now/#both-sides-now&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I was surprised on checking Rolling Stone Magazine&apos;s top 500 songs that Joni Mitchell&apos;s song &quot;Both Sides, Now&quot; wasn&apos;t in the top 50. For me, this song is even more mesmerising than any of Bob Dylan&apos;s incredible output ... and I&apos;m saying that as a firm fan of Dylan.&lt;/p&gt;
&lt;p&gt;The song suggests for me that there are different ways of looking at everything in life ... even conceptually simple things such as clouds can appear totally different to two observers.&lt;/p&gt;
&lt;p&gt;The lyrics seem to convey the idea that eventually we can learn to look at things from other perspectives, not just our own.&lt;/p&gt;
&lt;p&gt;That invokes a more mature and happier approach to life: with hindsight the things we argued about once can seem somewhat trivial compared to the way we look back on them.&lt;/p&gt;
&lt;p&gt;I imagine the author of this song looking back on an experience and thinking, I wish I hadn&apos;t argued that point, now I&apos;m older I can see it from a different perspective. All I achieved was conflict. And that conflict stopped me from enjoying and appreciating the differences I encountered.&lt;/p&gt;
&lt;h2&gt;Things that Aren&apos;t Conflicts &lt;a href=&quot;https://deliciousreverie.co.uk/posts/both-sides-now/#things-that-aren&apos;t-conflicts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I see arguments like I mentioned at the outset, I think, that&apos;s merely one way of looking at things.&lt;/p&gt;
&lt;p&gt;Is CSS-in-JS a mistake? For some people, it is. Totally unnecessary. For others, it&apos;s absolutely the best way to achieve the results they seek. Having worked in both camps, I can see there&apos;s no conflict in building for the web either way.&lt;/p&gt;
&lt;p&gt;Is JavaScript destroying the web? I&apos;ll say this again (though I am starting to sound like a stuck record!) that there&apos;s a lack of HTML and CSS skills among our peers, and that will need to be addressed at some point. But look at the things a dynamic language like JavaScript has enabled us to achieve. Without it, the web would be a much poorer place.&lt;/p&gt;
&lt;h2&gt;Looking Back &lt;a href=&quot;https://deliciousreverie.co.uk/posts/both-sides-now/#looking-back&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Conflict is paralysing. It stops anything useful from being achieved. It&apos;s far better to acknowledge the difference, respectfully share our opinion, and leave it at that.&lt;/p&gt;
&lt;p&gt;Starting now, and in years to come, if there are to be any, we ought to try to do the same. If we see someone voice an opinion that riles you, try to think, can I see it from both sides, now?&lt;/p&gt;
&lt;p&gt;https://open.spotify.com/track/1pjATX7sbd6Y4jMVqIvzHk&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Both Sides, Now&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Building a media player with Web Components</title><link>https://deliciousreverie.co.uk/posts/build-media-player-web-components/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/build-media-player-web-components/</guid><description>This past year I&apos;ve been involved in replatforming our media player to use media-chrome from Mux instead of JWPlayer. Here&apos;s how that went.</description><pubDate>Mon, 23 Dec 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;This past year I&apos;ve been involved in replatforming our media player to use media-chrome from Mux instead of JWPlayer. Here&apos;s how that went.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I hate JWPlayer with a passion. Everything that should be easy about it causes us as a development team headaches every day. Here&apos;s an example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let numberOfTimesChecked = 0;
function doSomething() {
  setTimeout(() =&amp;gt; {
    if(typeof jwplayer === &apos;function&apos; &amp;amp;&amp;amp; jwplayer().hasOwnProperty(&quot;play&quot;)) {
      doSomething();
    } else {
     numberOfTimesChecked += 1; 
     return;
    }
  }, 500)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re any kind of javascript developer you&apos;re screaming internally now, aren&apos;t you?&lt;/p&gt;
&lt;p&gt;This function uses recursion to fire the function again continually every half a second because &lt;em&gt;there&apos;s no way to verify that JWPlayer is ready&lt;/em&gt; before you call it.&lt;/p&gt;
&lt;p&gt;JWPlayer makes API calls to their CDN (another reason we&apos;re moving away: we have corporate clients who proactively block code delivery CDNs). These calls check whether the token we provide is valid and load extra code that&apos;s not included in the bundle (hls.js is loaded from &lt;em&gt;their&lt;/em&gt; domain using this method. Don&apos;t ask me whether the hls.js code has been modified, I have no idea).&lt;/p&gt;
&lt;p&gt;Yet, &lt;code&gt;jwplayer()&lt;/code&gt; provides no asynchronous API. Not only that, there&apos;s a significant delay between &lt;code&gt;jwplayer()&lt;/code&gt; being ready, and its child functions being ready, as you can see above, &lt;code&gt;jwplayer().play()&lt;/code&gt; might not be defined when we call it.&lt;/p&gt;
&lt;p&gt;This is stupid and is causing us to write spaghetti code to mitigate it&apos;s ... let&apos;s call them idiosyncrasies ... and I am still not sure how many users we&apos;re letting down because of that.&lt;/p&gt;
&lt;p&gt;So, good riddance JWPlayer.&lt;/p&gt;
&lt;h2&gt;A better way&lt;/h2&gt;
&lt;p&gt;If you&apos;re having similar struggles, I heartily suggest you &lt;a href=&quot;https://www.media-chrome.org/&quot;&gt;check out Media Chrome from the Mux team&lt;/a&gt;. This comprehensive suite of Web Components provides a lot more functionality which you can build on top of the native &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element. They even have elements for streaming video. We use their &lt;a href=&quot;https://www.mux.com/player&quot;&gt;Mux Player&lt;/a&gt; for decoding audio and video streams served from their platform.&lt;/p&gt;
&lt;p&gt;I really love that you can write a simple lines of JavaScript once you&apos;ve done &lt;code&gt;npm install media-chrome&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import &apos;media-chrome&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you can do the rest in HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;media-controller&amp;gt;
  &amp;lt;video
    slot=&quot;media&quot;
    src=&quot;https://stream.mux.com/DS00Spx1CV902MCtPj5WknGlR102V5HFkDe/high.mp4&quot;
  &amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;/media-controller&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;slot&lt;/code&gt; directive tells the &lt;code&gt;&amp;lt;media-controller&amp;gt;&lt;/code&gt; element you want to render this &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element in the &lt;code&gt;media&lt;/code&gt; slot.&lt;/p&gt;
&lt;p&gt;There are other slots for things like icons, poster images, and many other fancy things.&lt;/p&gt;
&lt;p&gt;If you&apos;re looking for a quick implementation you can run with, I&apos;d recommend taking a look at their website https://player.style, where you can copy and paste a theme straight into your HTML.&lt;/p&gt;
&lt;p&gt;Of course, our situation is a bit different in that we have to provide an API for our clients to use as well as integrations for our trackers to ensure qualifications can be achieved, and settings that are publicly available to customise the player.&lt;/p&gt;
&lt;p&gt;We also have live events as well as podcast content to think about.&lt;/p&gt;
&lt;p&gt;This code had so many moving parts and integrations that it&apos;s honestly a bit dizzying thinking about it all.&lt;/p&gt;
&lt;p&gt;But honestly we started with the basic idea that we wanted it to be super easy for people to use. We wanted them to load the script and add as few lines to the page as possible.&lt;/p&gt;
&lt;p&gt;I think we&apos;ve achieved that, the basic implementation is&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-media-player
  token=&quot;player-token&quot;
&amp;gt;
&amp;lt;/my-media-player&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along with the script tag, that&apos;s the basic API.&lt;/p&gt;
&lt;p&gt;I love the fact that this is native HTML. It hides so much complexity that the average user just doesn&apos;t need to think about.&lt;/p&gt;
&lt;p&gt;In the first instance we didn&apos;t want the player to fail silently when that&apos;s in our control. As long as they have included the JS, We have to provide some UI for occasions where the video can&apos;t be resolved.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This card shows when the provided &lt;code&gt;token&lt;/code&gt; can&apos;t be resolved to a specific video.&lt;/p&gt;
&lt;p&gt;There are other notable visual differences when a livestream is playing. Specifically this changes whether they can do things like seek or otherwise skip segments of the video:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Otherwise the chrome looks like this for a video:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;And this for audio:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;There&apos;s a huge amount of customisation available, both in what the user can provide when they instantiate the player, and in what they set up on the backend interface for options etc.&lt;/p&gt;
&lt;p&gt;For example, they might want to show some custom branding for their channel, so the player controls will adopt their custom colour. They can add a custom button into the player. They can allow or block downloads.&lt;/p&gt;
&lt;h2&gt;Mux elements are very well thought out&lt;/h2&gt;
&lt;p&gt;I really admire how thoughtful the team has been about making this player as adaptable as it can be.&lt;/p&gt;
&lt;p&gt;Just about all of their individual elements that make up this suite have a &lt;code&gt;&amp;lt;slot name=&quot;icon&quot;&amp;gt;&lt;/code&gt; so that you can override their default icons with a simple SVG element.&lt;/p&gt;
&lt;p&gt;They also provide a huge range of CSS variables, which allow you to customise a lot of the internal CSS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:host {
  --media-font-size: 24rem;
  --media-range-thumb-background: red;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These cascade down, so as here if you do this in your &lt;code&gt;:host {}&lt;/code&gt; block, all elements will adopt them.&lt;/p&gt;
&lt;p&gt;Honestly, this is fantastic. A lot of the CSS is hidden in the shadow DOM, but honestly I don&apos;t want access to all of the internals. I&apos;ve build this complex player without being restricted by that.&lt;/p&gt;
&lt;h2&gt;Events&lt;/h2&gt;
&lt;p&gt;We also provide an API so that users can send events into the player. For example, once you&apos;ve added the HTML code you can do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const player = document.querySelector(&apos;media-manager-player&apos;)

player?.addEventListener(&apos;play&apos;, () =&amp;gt; {
  player.dispatchEvent(new CustomEvent(&apos;pause&apos;))
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a totally contrived example but when a play even is detected we can pause it, or do any one of a number of events.&lt;/p&gt;
&lt;p&gt;But there was one I found particularly tricky: chapters.&lt;/p&gt;
&lt;p&gt;The use case here is that we need to expose the content of the chapters to users. We use this internally to inform users of how many chapters they need to watch before they can take a quiz which will enable them to get points towards their accreditation.&lt;/p&gt;
&lt;p&gt;But the Mux stuff is so straightforward that it handled the chapters content for me.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-video-player&amp;gt;
  &amp;lt;mux-video&amp;gt;
    &amp;lt;track type=&quot;chapters&quot; url=&quot;https://path/to/my/chapters.VTT&quot;&amp;gt;
  &amp;lt;/mux-video&amp;gt;
&amp;lt;/my-video-player&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you pass a VTT file to mux-video it fetches it and parses it to provide a chapters menu. However it doesn&apos;t expose that content in a way I can further transform or pass back outside of the player.&lt;/p&gt;
&lt;p&gt;So I implemented our own solution:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;myPlayer.dispatchEvent(new CustomEvent(&apos;getchapters&apos;))

myPlayer.addEventListener(&apos;chaptersdataupdated&apos;, (event) =&amp;gt; {
  console.log(event.detail) // JSON string of chapters
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Internally, this fetches the chapters, parses the VTT file and fires off the event. This means average users aren&apos;t burdened with the extra wait time of fetching the chapters a second time as the player instantiates.&lt;/p&gt;
&lt;p&gt;This project hasn&apos;t been without it&apos;s pitfalls and struggles especially since it&apos;s been in development for around a year, so significant (and very positive) upgrades needed to me merged in.&lt;/p&gt;
&lt;p&gt;We also found it a challenge to ensure that the code was as well tested as it can be. This is hard because most test runners assume components are instantiated in JS after the real DOM has loaded, and in this case that isn&apos;t true. I&apos;ve come up with a solution, and Storybook&apos;s support has got a lot better in it&apos;s latest release. But there are still significant gaps.&lt;/p&gt;
&lt;p&gt;You can find a &lt;a href=&quot;/posts/testing-web-components/&quot;&gt;full write up on the testing situation here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However I&apos;m so pleased to have stumbled across the media-chrome project, and to have in that time also got to grips with native Web Components.&lt;/p&gt;
&lt;p&gt;And yes, I did a case study for integration with Vue, React 17 and 18, and with Svelte, and the player rendered fine in all of them.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Building a media player with Web Components&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Augmenting the details element</title><link>https://deliciousreverie.co.uk/posts/augmenting-details-element/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/augmenting-details-element/</guid><description>I&apos;ve found the details element superbly useful lately. I added only one or two things to make this useful element suit two very different use cases</description><pubDate>Wed, 23 Jul 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;&lt;em&gt;I&apos;ve found the details element superbly useful lately. I added only one or two things to make this useful element suit two very different use cases&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I recently rebuilt some dropdowns with a very minimal amount of JavaScript, and most of that is to support Safari&apos;s lack of support for the &lt;code&gt;:marker&lt;/code&gt; pseudoelement.&lt;/p&gt;
&lt;p&gt;Previously, we had a structure similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;nav&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li&amp;gt;
      &amp;lt;button type=&quot;button&quot; aria-controls=&quot;mega-menu-one-dropdown&quot; aria-expanded=&quot;false&quot; id=&quot;mega-menu-one-button&quot;&amp;gt;
        link parent
        &amp;lt;svg class=&quot;caret&quot;&amp;gt;&amp;lt;/svg&amp;gt;
      &amp;lt;/a&amp;gt;
      &amp;lt;div id=&quot;mega-menu-one-dropdown&quot; aria-controlledby=&quot;mega-menu-one-button&quot; aria-expanded=&quot;false&quot;&amp;gt;
        // sub-nav content, which is huge
        &amp;lt;ul&amp;gt;
          &amp;lt;li&amp;gt;
            &amp;lt;a href=#&amp;gt;
              link child
            &amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
        &amp;lt;/ul&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/nav&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might imagine this was difficult to deal with and since it was generated dynamically, very hard to reason about.&lt;/p&gt;
&lt;p&gt;Next to that of course we had a bunch of JS that would do the following when one of the buttons were clicked:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Update the &lt;code&gt;aria-role&lt;/code&gt; on the button&lt;/li&gt;
&lt;li&gt;Rotate the SVG caret on the button&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;aria-role&lt;/code&gt; on the dropdown element&lt;/li&gt;
&lt;li&gt;Reset the &lt;code&gt;aria-role&lt;/code&gt; on other buttons&lt;/li&gt;
&lt;li&gt;Reset the &lt;code&gt;aria-role&lt;/code&gt; on other dropdown elements&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The HTML for something like this would be&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div
  id=&quot;mega-menu-one-dropdown&quot;
  aria-controlledby=&quot;mega-menu-one-button&quot;
  aria-expanded=&quot;false&quot;
  class=&quot;tw-hidden aria-expanded:tw-block&quot;
&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we didn&apos;t actually just do this. In my first iteration of JavaScript I neglected to notice that you can add modifier Tailwind classes to the HTML so that when &lt;code&gt;aria-role&lt;/code&gt;s change, different styling can be applied:&lt;/p&gt;
&lt;p&gt;Instead I was doing the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;button.addEventHandler(&quot;click&quot;, () =&amp;gt; {
  [&quot;tw-hidden&quot;, &quot;tw-block&quot;].forEach((classListItem) =&amp;gt;
    dropdown.classList.toggle(classListItem)
  );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yeah, probably don&apos;t do that.&lt;/p&gt;
&lt;p&gt;Since it received a design refresh, I took the opportunity to simplify it significantly.&lt;/p&gt;
&lt;h2&gt;The Refresh&lt;/h2&gt;
&lt;p&gt;Now with the details element the markup looks a lot more sane, although to be honest it&apos;s always going to be quite complicated:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;nav&amp;gt;
  &amp;lt;details&amp;gt;
    &amp;lt;summary&amp;gt;
      Link parent
      &amp;lt;svg class=&quot;caret&quot;&amp;gt;&amp;lt;/svg&amp;gt;
    &amp;lt;/summary&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;
          &amp;lt;a href=&quot;#&quot;&amp;gt; link child &amp;lt;/a&amp;gt;
        &amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/details&amp;gt;
&amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a lot easier to reason about, &lt;code&gt;aria&lt;/code&gt; roles are built in, and the functionality is identical to the user.&lt;/p&gt;
&lt;h2&gt;Safari&lt;/h2&gt;
&lt;p&gt;I&apos;m not going to belittle Safari here. It&apos;s a solid browser and Jen Simmons and the team have worked hard to get it to feature parity with other browsers. Their release cycle might be a bit slower but it&apos;s a formidable tool.&lt;/p&gt;
&lt;p&gt;I did find that I needed some extra JS to support just one thing: updating the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; caret style, which needs to rotate 180 degrees to indicate the collapsible section is open.&lt;/p&gt;
&lt;p&gt;I elected to scope this to Safari only. This is because I want the developer who comes to this file in 2 years time to instantly see which lines they can now delete:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @see https://caniuse.com/css-marker-pseudo
 * To polyfill the css ::marker pseudo element
 * so that SVG carets can rotate when &amp;lt;details&amp;gt; elements
 * are expanded. Can be deleted when this is supported in Safari
 */
const isSafari =
  navigator.userAgent.indexOf(&quot;Safari&quot;) &amp;gt; -1 &amp;amp;&amp;amp;
  navigator.userAgent.indexOf(&quot;Chrome&quot;) === -1;

detailsElement.addEventListener(&quot;toggle&quot;, () =&amp;gt; {
  isSafari &amp;amp;&amp;amp;
    detailsElement.classList.toggle(&quot;details-open&quot;, detailsElement.open);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, I&apos;m a fan of clear signposting.&lt;/p&gt;
&lt;p&gt;Now that we have this class, we can use it to toggle the rotation on the SVG.&lt;/p&gt;
&lt;h2&gt;Styling&lt;/h2&gt;
&lt;p&gt;Unfortunately Tailwind v3 doesn&apos;t seem to support the &lt;code&gt;open&lt;/code&gt; attribute natively, and we don&apos;t have capacity to upgrade to v4 just now. So I had to bake this into the CSS like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;details:open &amp;gt; summary svg.caret {
  @apply tw-rotate-180;
}
/* Safari workaround: 
 * use .details-open class via JS instead of :open pseudo-class 
 * @see https://caniuse.com/css-marker-pseudo
 * Remove if safari now supports this
*/
.details-open &amp;gt; summary svg.caret {
  @apply tw-rotate-180;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Closing other details&lt;/h2&gt;
&lt;p&gt;The one thing I did add was that when you open one menu item, the others should close.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener(&quot;click&quot;, (event) =&amp;gt; {
  detailsElements.forEach((detailsElement) =&amp;gt; {
    if (event.target.closest(&quot;details&quot;) === detailsElement) {
      return;
    }
    detailsElement.open = false;
    isSafari &amp;amp;&amp;amp; detailsElement.classList.remove(&quot;details-open&quot;);
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of handling this when a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element is clicked however, I put it on a document listener. Reason being that I wanted to also close the other elements when someone clicks elsewhere on the document:&lt;/p&gt;
&lt;h2&gt;Details is great!&lt;/h2&gt;
&lt;p&gt;When we roll this out to our sites it&apos;ll achieve the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It&apos;ll require less custom JS&lt;/li&gt;
&lt;li&gt;The code will be easier to follow at a glance&lt;/li&gt;
&lt;li&gt;Browsers will handle aria&lt;/li&gt;
&lt;li&gt;There&apos;s less chance it&apos;ll break when changed&lt;/li&gt;
&lt;li&gt;The elements have more semantic meaning&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I&apos;m so looking forward to more elements like this, including spicy sections, and especially carousels.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Augmenting the details element&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Building a Frontend With GatsbyJS</title><link>https://deliciousreverie.co.uk/posts/building-frontend-with-gatsbyjs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/building-frontend-with-gatsbyjs/</guid><description>This article was originally published for Webiny&apos;s technical documentation portal and takes readers through how to build a frontend for Webiny Headless CMS with Gatsby. </description><pubDate>Sun, 02 Jan 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In this article we’ll learn how to integrate a GatsbyJS application that uses Webiny as a Headless CMS. We’ll go through the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting started with Webiny&lt;/li&gt;
&lt;li&gt;Setting up a GatsbyJS project&lt;/li&gt;
&lt;li&gt;Fetching data&lt;/li&gt;
&lt;li&gt;Rendering data using Gatsby’s data layer&lt;/li&gt;
&lt;li&gt;Rendering Webiny’s rich text field&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webiny.com/docs/headless-cms/integrations/gatsby&quot;&gt;Read the full article&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Building a Frontend With GatsbyJS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Building a Frontend With NextJS</title><link>https://deliciousreverie.co.uk/posts/building-frontend-with-nextjs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/building-frontend-with-nextjs/</guid><description>This was originally published for Webiny&apos;s documentation portal and teaches readers how build a frontend for Webiny Headless CMS with NextJS.</description><pubDate>Mon, 03 Jan 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In this article we’ll learn how to integrate a NextJS application that uses Webiny as a Headless CMS. We’ll go through the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting started with Webiny&lt;/li&gt;
&lt;li&gt;Setting up a NextJS project&lt;/li&gt;
&lt;li&gt;Fetching data&lt;/li&gt;
&lt;li&gt;Rendering Webiny’s rich text field&lt;/li&gt;
&lt;li&gt;Using NextJS preview mode&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webiny.com/docs/headless-cms/integrations/nextjs&quot;&gt;Read the full article&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Building a Frontend With NextJS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Building an Accordion Component with React Hooks</title><link>https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/</guid><description>Originally published in print for Net Magazine issue 320. I wanted to showcase how I have begun using React Hooks, and hilight some best practices to follow for better accessibility. </description><pubDate>Sat, 15 Jun 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In this article, we takes a deep dive into creating an accordion module for React that renders on the server, and that works in Internet Explorer 11.&lt;/p&gt;
&lt;p&gt;On a recent project I was involved in creating this Accordion component with a few key requirements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It should provide collapsible section functionality for our users, a significant portion of whom are still on Internet Explorer 11&lt;/li&gt;
&lt;li&gt;It should be as accessible as possible, especially for assistive technology users&lt;/li&gt;
&lt;li&gt;It should show content even if the user hasn&apos;t been able to download JavaScript, or has it turned off&lt;/li&gt;
&lt;li&gt;It can be used multiple times on a page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&apos;s get started!&lt;/p&gt;
&lt;h2&gt;Basic Setup &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#basic-setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&apos;s the basic layout for the module:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;
const Accordion = (props) =&amp;gt; {
  return (
    &amp;lt;li&amp;gt;
      &amp;lt;h3&amp;gt;
        &amp;lt;button&amp;gt;
          {question}
        &amp;lt;/button&amp;gt;
      &amp;lt;/h3&amp;gt;
      &amp;lt;div&amp;gt;
      {answer}
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
  )
}
export default Accordion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will render a basic section and can accept our content labelled question and answer. Outside the accordion, I will define an unordered list, &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, to wrap around the items.&lt;/p&gt;
&lt;p&gt;The elements used here are one of the suggestions made by Sara Souiedan in her blog post &quot;How do you mark up an accordion?&quot;. It&apos;s well worth a read because it dives deep into the semantic meaning of these elements.&lt;/p&gt;
&lt;p&gt;You can find her excellent article here: &lt;a href=&quot;https://www.sarasoueidan.com/blog/accordion-markup/&quot;&gt;https://www.sarasoueidan.com/blog/accordion-markup/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;IE Support Not Needed? Use &amp;lt;details&amp;gt; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#ie-support-not-needed-use-lessdetailsgreater&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On Sara&apos;s blog post above, using the &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt; elements here for more semantic markup is discussed. This is a HTML-native accordion element!&lt;/p&gt;
&lt;p&gt;If you don&apos;t need to support Internet Explorer 11, use these and skip most of this tutorial: you&apos;ll see that the accordion collapses and opens without any css or javascript.&lt;/p&gt;
&lt;h2&gt;Functionality &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#functionality&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, I&apos;m going to import our first hook. Hooks allow us to write React lifecyle methods without classes. This means our code can be more functional, or at least not a mix of OO and functional programming.&lt;/p&gt;
&lt;p&gt;First, I need to import the &lt;code&gt;useState&lt;/code&gt; hook along with React from the React library:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, I set my state items. There are 2 because setContentVisible will be a function to update &lt;code&gt;isContentVisible&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  let [isContentVisible, setContentVisible] = useState(true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m setting a default of true here because I&apos;m using server-side-rendering, so that users without JavaScript (and search engines) can see this content. Now I can use this state directly on my button:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button onClick={() =&amp;gt; setContentVisible(isContentVisible = !isContentVisible)}&amp;gt;
{question}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Doesn&apos;t this look a lot more straightforward than writing a function before the render method?&lt;/p&gt;
&lt;h2&gt;Screen Reader Assistance &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#screen-reader-assistance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Next, I&apos;m going to add some aria roles to the accordion to indicate to users when the component&apos;s content area is visible or not:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;li&amp;gt;
      &amp;lt;h3&amp;gt;
        &amp;lt;button
            onClick={() =&amp;gt; setContentVisible(isContentVisible = !isContentVisible)}
            aria-controls=&quot;accordion-1&quot;
            aria-expanded={isContentVisible}
        &amp;gt;
          {question}
        &amp;lt;/button&amp;gt;
      &amp;lt;/h3&amp;gt;
      &amp;lt;div&amp;gt;
      &amp;lt;ContentContainer
          aria-hidden={!isContentVisible}
          id=&quot;accordion-1&quot;
        /&amp;gt;
      {answer}
      &amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives the screen reader ability to hear which section is open or closed, and the ability to open or close the accordion.&lt;/p&gt;
&lt;p&gt;But that id isn&apos;t going to cut it for more than one use on a page.&lt;/p&gt;
&lt;p&gt;To do that, we need to set a unique id of the accordion item so we can show the relationship between the control and content elements.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const elementId = (((1 + Math.random()) * 0x10000) | 0)
    .toString(16)
    .substring(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I&apos;ve replaced &lt;code&gt;id=&quot;accordion-1&quot;&lt;/code&gt; with &lt;code&gt;id={elementId}&lt;/code&gt; I should have a fairly unique ID that links up the content and the button.&lt;/p&gt;
&lt;h2&gt;The Slings &amp;amp; Arrows of Outrageous Smooth Scrolling &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#the-slings-and-arrows-of-outrageous-smooth-scrolling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the requirements you might have with an accordion of this kind is that when the accordion opens / closes, the user is scrolled to the content area or the title.&lt;/p&gt;
&lt;p&gt;You can define a separate toggleElement function to get the ID of the current element, and scroll to the content area.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const domElementTop = elementId.offsetTop - 150
  const toggleVisibility = () =&amp;gt; {
    window.setTimeout(function() {
      window.scrollTo({ top: domElementTop, behavior: &quot;smooth&quot; })
    }, 0)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s an IE11 caveat with this though. IE calculates &lt;code&gt;scrollTo&lt;/code&gt; differently, with a px value instead of a string or ID. You can use scrollTop instead, but this doesn&apos;t allow you to have smooth scrolling behaviour which is a nicer effect for users. If you want to support IE11 with smooth scrolling, you can use smoothscroll-polyfill and call that before &lt;code&gt;window.scrollTo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There&apos;s a bug in iOS Safari too! If you want to support Safari on iOS (particularly some iPad versions) it&apos;s advisable to set a timeout when you start scrolling:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    window.setTimeout(() =&amp;gt; {
      // call the polyfill (to support IE11)
      smoothscroll.polyfill()
      window.scrollTo({ top: domElementTop, behavior: &apos;smooth&apos; })
    }, 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Showing &amp;amp; Hiding Content &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#showing-and-hiding-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The last item we need to take care of is showing / hiding the content area. Now, we could do it this way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{isContentVisible &amp;amp;&amp;amp; (
    &amp;lt;div
       aria-hidden={!isContentVisible}
       id={`content-${AccordionIdentifier}`}
    &amp;gt;
      {answer}
    &amp;lt;/div&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the use of the double-ampersand. If the isContentVisible is true, the following block (inside brackets) will show.&lt;/p&gt;
&lt;p&gt;You can also do the show/hide with a class attributes, using the display property to show &amp;amp; hide them using CSS.&lt;/p&gt;
&lt;p&gt;Lastly, I want to make sure that when my user has got the JavaScript bundle and the page has been rehydrated (see box, &quot;What is Rehydration&quot;), the content is collapsed, ready for them to interact with. I can do that with the useEffect hook, but I need to make sure I call useEffect only once, similar to how componentDidMount was used.&lt;/p&gt;
&lt;p&gt;First, import that from React the same way as the &lt;code&gt;useState&lt;/code&gt; hook:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React, { useState, useEffect } from &quot;react&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then reverse the value of &lt;code&gt;setContentVisible&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  useEffect(() =&amp;gt; {
    setContentVisible(isContentVisible = !isContentVisible)
  }, [])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Did you see the extra square brackets (&lt;code&gt;[]&lt;/code&gt;) at the end? By passing an empty array to the useEffect hook we can call it only once. If we didn&apos;t do that, the state would be updated every time the component updated in some way.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I think using React Hooks is a simpler and tidier workflow that helps our code to be quicker to understand and more flexible. There are some significant changes to the way we use React, but understanding the differences in my opinion is only going to improve the way you write your code.&lt;/p&gt;
&lt;p&gt;You can find this example on Code Sandbox: &lt;a href=&quot;https://codesandbox.io/s/wqnmql8l78&quot;&gt;https://codesandbox.io/s/wqnmql8l78&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: What is Rehydration? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#boxout:-what-is-rehydration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Think of a carton of orange juice. You&apos;ll often see on the side of the packet &quot;from concentrate&quot; or &quot;rehydrated&quot;.&lt;/p&gt;
&lt;p&gt;This means they&apos;ve extracted the water from the juice to condense it. In this way it can be transported more easily, in greater quantities. The water is added back in at the destination.&lt;/p&gt;
&lt;p&gt;Rehydration in JavaScript can be compared with this method. With some frameworks, notably GatsbyJS, our page is built in HTML and rendered in the browser. Then, once the JavaScript is loaded, it replaces the HTML with it&apos;s JavaScript version of the page.&lt;/p&gt;
&lt;p&gt;The advantage is a fully interactive application that is better for SEO and for users.&lt;/p&gt;
&lt;p&gt;There are still some key puzzles to solve with rehydration. I&apos;m looking forward to seeing a time when only parts of a page that need interactivity are rehydrated, instead of having to replace the entire page. But it&apos;s a great way of making faster and more resilient interactive experiences.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: MVT, Object-Oriented or Functional Programming? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/building-an-accordion-with-react-hooks/#boxout:-mvt-object-oriented-or-functional-programming&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Managing the code of large applications where several people are contributing over the course of longer periods of time tends to be difficult. In fact any sufficiently complicated system tends to break down over time.&lt;/p&gt;
&lt;p&gt;To combat this tendency in code, people have tried several different approaches. The ones I&apos;m familiar with are MVC, OO (object-oriented) and Functional.&lt;/p&gt;
&lt;p&gt;As far as I know, Angular 1 introduced the MVC principle to the frontend. It tried to keep separate the views (or templates), models (interpretations of data) and controllers (the functional parts) separate.&lt;/p&gt;
&lt;p&gt;This proved in many cases to be a burden on the overall code that was sent down to the client. A few later frameworks experimented with Object Oriented programming, which works great for languages like Ruby and PHP, and can be successfully implemented with JavaScript too.&lt;/p&gt;
&lt;p&gt;However, JavaScript doesn&apos;t really work in an object-oriented way under the hood. The class keyword was a wrapper around some of the prototypes of the language.&lt;/p&gt;
&lt;p&gt;In an effort to work with the language instead of imposing an artificial structure on top of it, functional programming has been growing in popularity in recent years.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Building an Accordion Component with React Hooks&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How to Cache and De-duplicate Fetch Requests</title><link>https://deliciousreverie.co.uk/posts/cache-and-dedup-requests/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/cache-and-dedup-requests/</guid><description>If you have an application which makes many requests to the same API you can use a key to cache and de-duplicate requests. Here&apos;s how to do it.</description><pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;If you have an application which makes many requests to the same API you can use a key to cache and de-duplicate requests.&lt;/p&gt;
&lt;h2&gt;Why might this happen?&lt;/h2&gt;
&lt;p&gt;Ideally you shouldn&apos;t encounter this situation. Your data layer or state should be a separate entity which components can subscribe to.&lt;/p&gt;
&lt;p&gt;This is what React Context and similar solutions do: they hoist the state to a separate layer which can then be passed to each component.&lt;/p&gt;
&lt;p&gt;But you know that already.&lt;/p&gt;
&lt;p&gt;If you&apos;re using vanilla JavaScript you have some other options. You can use &lt;a href=&quot;https://tanstack.com/store/latest/docs/framework/react/examples/simple&quot;&gt;TanStack Store&lt;/a&gt; or some other third party.&lt;/p&gt;
&lt;p&gt;But where&apos;s the fun in that?&lt;/p&gt;
&lt;h2&gt;The implementation&lt;/h2&gt;
&lt;p&gt;Imagine we have a component that renders a chart. It should fetch data from our API to populate that data.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import retrieveData from &quot;@api/retrieve-data&quot;;

async function myChart() {
  const totals = await retrieveData(&quot;getTotals&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple enough.&lt;/p&gt;
&lt;p&gt;This is where it gets complicated. This is one component in a complex dashboard that renders 10-20 different charts.&lt;/p&gt;
&lt;p&gt;There are inputs on this chart too: users chan change date ranges, apply filters and all that good stuff.&lt;/p&gt;
&lt;p&gt;Add to that there are several different pages to this dashboard, each of which involves a server re-render. There&apos;s no possibility of using a frontend router.&lt;/p&gt;
&lt;p&gt;What would you do?&lt;/p&gt;
&lt;p&gt;I created a CustomEvent to enable my components to re-render when a filter changed.&lt;/p&gt;
&lt;p&gt;Dispatching the event from filter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filterElement.addEventListener(&quot;change&quot;, () =&amp;gt; {
  // ... (other event stuff)
  window.dispatchEvent(new CustomEvent(&quot;refreshState&quot;))
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reacting to it in the components:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import retrieveData from &quot;@api/retrieve-data&quot;;

async function myChart() {
  const totals = await retrieveData(&quot;getTotals&quot;);
}
window.addEventListener(&quot;refreshState&quot;, () =&amp;gt; myChart());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This calls the chart function again when the filter changes. It allows me to keep my component focused on the UI and abstract state to the &lt;code&gt;retrieveData&lt;/code&gt; component.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at that now.&lt;/p&gt;
&lt;h2&gt;Retrieving Data&lt;/h2&gt;
&lt;p&gt;This is the basic implementation I ended up with.&lt;/p&gt;
&lt;p&gt;I&apos;ve leant on the URL query params as a central location to store updated state. That way I can de-couple the user input filters from the fetch request and still have them easily accessible:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function retrieveData(requestType, args) {
  const urlParams = new URLSearchParams(window.location.search);

  const dateFromFilter = urlParams.get(&quot;datefrom&quot;);
  const dateToFilter = urlParams.get(&quot;datefrom&quot;);
  const user = getUser();

  const cacheKey = `dashboard-action=${requestType}-user=${user}-from=${dateToFilter}-to=${dateToFilter}`;

  const cachedData = localStorage.getItem(cacheKey);

  if(cachedData) {
    return cachedData;
  }
  const freshData = await callApi(requestType, { user, from: dateFromFilter to: dateToFilter });

  localStorage.setItem(
    cacheKey,
    JSON.stringify(freshData)
  )

  return freshData;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So you can see we&apos;re setting a cache and returning that cached data if it still exists, otherwise fetching from the API and storing that data.&lt;/p&gt;
&lt;p&gt;This is a good start, but it&apos;s going to get more complex.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because it takes time for localStorage to save items. Meaning if we initiate 15 requests concurrently, each one could make an API request when it doesn&apos;t need to.&lt;/p&gt;
&lt;p&gt;Let&apos;s dive into how we de-duplicate those requests.&lt;/p&gt;
&lt;h2&gt;De-duplicate requests&lt;/h2&gt;
&lt;p&gt;If we have a dozen or so components on a page, and they sometimes use the &lt;em&gt;same&lt;/em&gt; data from the API, how do we reduce load on the API?&lt;/p&gt;
&lt;p&gt;We can use the &lt;code&gt;cacheKey&lt;/code&gt; above to store a request, and then create a queue for the requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const inFlightRequests = new Map();

async function retrieveData(requestType, args) {
  const urlParams = new URLSearchParams(window.location.search);

  // ...

  const cacheKey = `dashboard-action=${requestType}-user=${user}-from=${dateToFilter}-to=${dateToFilter}`;

  // 1. Request is in progress, return that data
  if (inFlightRequests.has(cacheKey)) {
    return inFlightRequests.get(cacheKey);
  }

  // 2. If we have cached data, return that
  const cachedData = localStorage.getItem(cacheKey);

  if(cachedData) {
    return cachedData;
  }

  // 3. IF not, get fresh data
  const freshData = await callApi(requestType, { user, from: dateFromFilter to: dateToFilter });

  // Then set everything else up
  inFlightRequests.set(cacheKey, retrievedData);

  localStorage.setItem(
    cacheKey,
    JSON.stringify(freshData)
  )

  inFlightRequests.delete(cacheKey);

  return freshData;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;inFlightRequests&lt;/code&gt; lives in the module scope which means it persists when the function is called.&lt;/p&gt;
&lt;p&gt;It&apos;s also a &lt;code&gt;Map&lt;/code&gt;, in which each item is stored by key-value pairs in which keys are unique. You can&apos;t have two keys the same in a &lt;code&gt;Map&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map&quot;&gt;More details here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Expiring the Cache&lt;/h2&gt;
&lt;p&gt;Last bit is how to make sure we&apos;re not returning stale data. This is based on a few assumptions and might be different in your case. But I&apos;ve gone for 30 seconds.&lt;/p&gt;
&lt;p&gt;For me, thirty seconds is enough time for users to load a page, realise they&apos;re on the wrong page, and navigate to a different one, or to change their filters a few times before they figure out what data they actually want to see.&lt;/p&gt;
&lt;p&gt;Also if the data is updated, I&apos;m guessing it&apos;ll take more than 30 seconds to do that.&lt;/p&gt;
&lt;p&gt;Let&apos;s see how we can expire the cache:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function retrieveData() {
  // ...

  // Not using Temporal? You should look into that.
  const now = Temporal.Now.instant();

  localStorage.setItem(
    cacheKey,
    JSON.stringify({
      timestamp: now,
      data,
    })
  );
  if (cachedData) {
    const parsedCachedData = JSON.parse(cachedData);
    const thirtySecondsAgo = now.subtract({ seconds: 30 });

    if (cachedTime &amp;lt;= thirtySecondsAgo) {
      localStorage.removeItem(cacheKey);
      return;
    }
    return parsedCachedData.data;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now we have a timestamp which we can use to decide if the localStorage item is stale and discard it.&lt;/p&gt;
&lt;p&gt;A word on Temporal. I&apos;m using a polyfill alongside the browser implementation just now. It&apos;ll be implemented soon &lt;a href=&quot;https://tc39.es/proposal-temporal/#sec-temporal-objects&quot;&gt;now it&apos;s at Stage 3&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Being able to cache and de-duplicate requests has reduced API calls on certain pages of my application from 13 to 3.&lt;/p&gt;
&lt;p&gt;Like I said at the start, hopefully you won&apos;t ever have to implement this yourself. But for those of us who are implementing complex applications in vanilla JS, I hope this helps.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How to Cache and De-duplicate Fetch Requests&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Capture and Minimise Errors in your React Applications</title><link>https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/</guid><description>If we we&apos;ve spent any time developing, specifically with JavaScript, we&apos;ve seen the White Screen of Death. Nothing renders to the screen, and the only way of investigating the issue we caused is by opening the console and looking at the stack trace. </description><pubDate>Fri, 15 Mar 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;We&apos;ve all done it. Whether it&apos;s by forgetting to close a function, misspelling a variable or just plain old doing it wrong...&lt;/p&gt;
&lt;p&gt;If we we&apos;ve spent any time developing, specifically with JavaScript, we&apos;ve seen the White Screen of Death. Nothing renders to the screen, and the only way of investigating the issue we caused is by opening the console and looking at the stack trace.&lt;/p&gt;
&lt;p&gt;But what if that error occurs in production? On someone elses&apos; machine? If this is happening (chances are it is) you might be losing a large segment of potential customers (How many? Nobody knows!), not to mention the fact that you have absolutely no visibility about the nature of the error, so debugging and fixing it becomes very difficult.&lt;/p&gt;
&lt;p&gt;In this tutorial we&apos;re going to dive into React&apos;s ErrorBoundary API to see how it can be used effectively to (1) stop the app from crashing, (2) provide a fallback, and (3) capture data for analysis later.&lt;/p&gt;
&lt;p&gt;This article was originally published in Net Magazine issue 316.&lt;/p&gt;
&lt;h2&gt;Let&apos;s Get Started! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#let&apos;s-get-started!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To save this tutorial from being too long, I&apos;ve included the code for the above on GitHub at &lt;a href=&quot;https://github.com/endymion1818/netmag-react-error-reporting&quot;&gt;https://github.com/endymion1818/netmag-react-error-reporting&lt;/a&gt;. It&apos;s a basic create-react-app setup with axios hooked up to get some data from the Star Wars API. However, there&apos;s a deliberately placed issue in the &lt;code&gt;render()&lt;/code&gt; method that&apos;s causing the app to crash.&lt;/p&gt;
&lt;p&gt;To see this code in action, download or clone the repo. Open this directory in your terminal and install dependencies by running npm i.&lt;/p&gt;
&lt;p&gt;You can run the app in development mode by typing npm start. It&apos;s time to take a closer look at what&apos;s happening here.&lt;/p&gt;
&lt;h2&gt;Error Boundary API &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#error-boundary-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ErrorBoundary API allows us to create a component that wraps around our whole app, an individual component or both. Then, if anything in that component fails, we can define an error message - or a replacement UI component - to appear instead of our broken one.&lt;/p&gt;
&lt;p&gt;We&apos;re going to create a new file, FormErrorBoundary.js and add some code there. This does the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Defines some state so that we can decide what to do if there is an exception&lt;/li&gt;
&lt;li&gt;Use a built-in React function to set state if there is an error&lt;/li&gt;
&lt;li&gt;Send the error message somewhere we can examine and track it&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;export default class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false }
  }
  static getDerivedStateFromError(error) {
    return { hasError: true }
  }
  componentDidCatch(error, errorInfo) {
  }
  // continued below ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&apos;s provide our fallback UI component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  // ... continued from above

  render() {
    const { children } = this.props

    if (this.state.hasError) {
      return (
        &amp;lt;div&amp;gt;
          &amp;lt;h2&amp;gt;Something went wrong with our app!&amp;lt;/h2&amp;gt;
          &amp;lt;p&amp;gt;We&apos;re aware of the problem and we&apos;re working hard to fix it.&amp;lt;/p&amp;gt;
          &amp;lt;p&amp;gt;In the meantime, you can reach us by phone or email.&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      )
    }
    return children
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&apos;s import that into our form, and wrap it around the rest of the component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import ErrorBoundary from &apos;./ErrorBoundary&apos; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now if an exception occurs in that component, the visitor will see this message instead. You can trigger it on your machine easily by leaving a tag unclosed or doing something else you know would ordinarily break your app.&lt;/p&gt;
&lt;p&gt;But, how do we capture what has caused our error so we can fix it?&lt;/p&gt;
&lt;h2&gt;Grab, Bag and Tag &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#grab-bag-and-tag&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where a logging service comes in. Now, inside the &lt;code&gt;componentDidCatch()&lt;/code&gt; method of our ErrorBoundary component, we can send the exception data to a 3rd party service.&lt;/p&gt;
&lt;p&gt;For Enterprise level applications, I&apos;ve enjoyed using Splunk. Splunk is built for more than error reporting and I&apos;ve seen people use it as a conversion metric tool as well. Splunk&apos;s strength is in the vast volumes of data it can collect. It also allows you to visualise your data in charts so you can track trends and analyse data more easily.&lt;/p&gt;
&lt;p&gt;However, for smaller applications Splunk might be a bit overkill. I&apos;ve recently been introduced to Sentry, which offers a simpler set of tools that allow you to collect, categorise, monitor and track your issues.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sentry.io/for/javascript/&quot;&gt;Sentry&lt;/a&gt; has a free tier for developers and is really easy to get started with.&lt;/p&gt;
&lt;h2&gt;Error Reporting with Sentry &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#error-reporting-with-sentry&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, let&apos;s assume that - even though you&apos;ve tested your app thoroughly - you suspect there are errors happening in production. Or perhaps you want to find out for certain if there are any.&lt;/p&gt;
&lt;p&gt;Let&apos;s add Sentry to this project.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install @sentry/browser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gets the Sentry package which we can now import into our ErrorBoundary, and initialise with our credentials to enable Sentry to recieve our errors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as Sentry from &quot;@sentry/browser&quot;

Sentry.init({
  dsn: &quot;your-dsn-here&quot;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Update the &lt;code&gt;componentDidCatch()&lt;/code&gt; function with the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  componentDidCatch(error, errorInfo) {
    Sentry.withScope(scope =&amp;gt; {
      Object.keys(errorInfo).forEach(key =&amp;gt; {
        scope.setExtra(key, errorInfo[key])
      })
      Sentry.captureException(error)
    })
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now each time an error occurs, the data will be sent to Sentry, and you can even recieve an email alert!&lt;/p&gt;
&lt;h2&gt;Let&apos;s see it In Action! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#let&apos;s-see-it-in-action!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Are you ready to see what we&apos;ve been doing? To do that, we need to build our application, and serve it somewhere - ideally your local computer. This step involves installing a local javaScript web server to your computer.&lt;/p&gt;
&lt;p&gt;First, in your terminal, run &lt;code&gt;npm install --global sentry&lt;/code&gt;. Next, from your project directory, run npm build. This builds the production-ready app. Now we can serve this folder locally by running serve -s build. Opening up a browser and typing &lt;a href=&quot;https://localhost:5000/&quot;&gt;https://localhost:5000&lt;/a&gt; and you should be able to see our beautiful error message!&lt;/p&gt;
&lt;p&gt;Congratulations! You are no longer in the dark about your JavaScript errors. Now you can increase the usefulness of your app and improve your stability by not only providing a fallback state for your app, but also by reporting and squashing all of your remaining bugs.&lt;/p&gt;
&lt;h2&gt;React Testing Caveats &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#react-testing-caveats&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Catching and reporting errors in the way described in this article does have one caveat: the ErrorBoundary API is only capable of capturing errors that happen in your app. So errors could occur before your app is loaded that aren&apos;t captured.&lt;/p&gt;
&lt;p&gt;Sentry specifically mentions this in their document, stating that their error reporting tool should be initialised as soon as possible in your application.&lt;/p&gt;
&lt;p&gt;On some projects this is harder to achieve than others without some serious hacking about. On projects with server-side-rendering I&apos;ve been able to write the script into Helmet. That way, the error can be captured by using the window.onerror browser API instead of React&apos;s built-in API that we&apos;re discussing in the article:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as Sentry from &quot;@sentry/browser&quot;
// ...
&amp;lt;Helmet
	    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;{`
        {
           Sentry.init({
  				dsn: &quot;your-dsn-here&quot;
			})
            window.onerror = Sentry.withScope(scope =&amp;gt; {
      			Object.keys(errorInfo).forEach(key =&amp;gt; {
        			scope.setExtra(key, errorInfo[key])
      			})
      			Sentry.captureException(error)
    		})
        }
    `}&amp;lt;/script&amp;gt;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;--&lt;/p&gt;
&lt;h2&gt;Beware of Non-Standard Error Reporting &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#beware-of-non-standard-error-reporting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It may not surprise you to know that the way browsers report errors differs across different rendering engines and implementations. Sometimes, the errorObject object isn&apos;t defined, so you&apos;ll likely get some errors that aren&apos;t that helpful. Most will show you a really helpful level of detail, down to the exact line of code where the error occurred. But don&apos;t expect the same level of reporting from every browser. This doesn&apos;t mean Internet Explorer either. Safari only recently introduced some better error reporting practices. There&apos;s a comprehensive table on Sentry&apos;s blog to identify the inconsistencies, as well as implementing a workaround: &lt;a href=&quot;https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror.html#browser-compatibility&quot;&gt;https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror.html#browser-compatibility&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Errors or Exceptions? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/capture-minimise-errors-in-react-applications/#errors-or-exceptions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You may have heard situations where your application fail termed Exceptions instead of errors. The reason for that is that they&apos;re not errors insofar as a problem with the language but instead exceptional results from the way that language has been used. This means the problem has originated with something that is not driven by by pure logic ... that means you or I. Therefore they are exceptional results from the way you have applied the language.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Capture and Minimise Errors in your React Applications&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Implementing a carousel with classes only.</title><link>https://deliciousreverie.co.uk/posts/carousel-implementation-classes-only/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/carousel-implementation-classes-only/</guid><description>Last year I built a carousel project and showcased it here. I&apos;ve just updated the code I used so that you can implement a carousel by only adding the class name to the parent element. Here&apos;s a breakdown.</description><pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Last year I built a carousel project and showcased it here. I&apos;ve just updated the code I used so that you can implement a carousel by only adding the class name to the parent element. Here&apos;s a breakdown.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the &lt;a href=&quot;/posts/lets-build-a-carousel&quot;&gt;previous article where I implemented a carousel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The thing with that project is that although I was 90% of the way there, I didn&apos;t realise that there were certain limitations with the large monolith it would be implemented in. I&apos;m working on a Drupal site, and we need to sometimes initialise a carousel on a queue of items. When you create the queue you only have access to the class names of the wrapper. I recently got the chance to revisit this code and implement it so it could work in this setting.&lt;/p&gt;
&lt;h2&gt;Objective&lt;/h2&gt;
&lt;p&gt;The basic objective is to be able to have markup like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul class=&quot;carousel&quot;&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;img src=&quot;my-image.png&quot;&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;Title&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;Some text&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;
    &amp;lt;img src=&quot;my-image-2.png&quot;&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;Another Title&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;Some more text&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m not arguing semantics here, this is Drupal: the basic premise is that I don&apos;t have control of the HTML structure; and in fact this is vastly simplified because Drupal seems to inject a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; into the DOM every time someone on the planet says &quot;React&quot;.&lt;/p&gt;
&lt;p&gt;I also wanted to have some customisations for whoever is implementing a carousel, so they could choose to have navigation previous / next arrows, and also indicators (the little dots that typically show at the bottom of the carousel to indicate which item in the list is currently active), and also be able to customise the rotation speed.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;carousel carousel-speed-4000 carousel-show-indicators carousel-show-navigation-buttons&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s get to it!&lt;/p&gt;
&lt;h3&gt;Instantiation&lt;/h3&gt;
&lt;p&gt;Since this specifically for a Drupal project, I needed to query the DOM element using Drupal&apos;s &lt;code&gt;once()&lt;/code&gt; method, so there&apos;s an IIFE at the bottom of the file that provides the integration:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function (Drupal, once) {
  Drupal.behaviors.carousel = {
    attach() {

      once(&quot;create-carousel&quot;, &quot;.carousel&quot;).forEach(element =&amp;gt; {
        createCarousel(element);
      });
    }
  }
})(Drupal, once);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;re not using Drupal, then you can of course just use &lt;code&gt;document.querySelectorAll()&lt;/code&gt; as normal.&lt;/p&gt;
&lt;p&gt;There&apos;s another trick to this: Drupal loads in sections of the DOM separately, so the carousel items might not be present in the DOM initially. This is the main case for using &lt;code&gt;once()&lt;/code&gt;, but it also means that we have to &lt;em&gt;start&lt;/em&gt; with the DOM elements &lt;em&gt;hidden&lt;/em&gt; to begin with.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function (Drupal, once) {
  Drupal.behaviors.carousel = {
    attach() {

      once(&quot;create-carousel&quot;, &quot;.carousel&quot;).forEach(element =&amp;gt; {
        createCarousel(element);
        element.classList.remove(&apos;tw-hidden&apos;) // or element.style.display = &apos;block&apos;
      });
    }
  }
})(Drupal, once);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures that the elements are hidden until the carousel is initialised, so we don&apos;t get a paint where all the elements appear stacked one on top of another.&lt;/p&gt;
&lt;h2&gt;Setting heights&lt;/h2&gt;
&lt;p&gt;I&apos;m a big believer that carousels should be uniform height. Nobody wants to be reading some copy below a carousel only for it to shift up and down every 3 seconds. But since the items are being created by users who have no regard for image sizes and length of content, we can&apos;t guarantee the height will be uniform.&lt;/p&gt;
&lt;p&gt;I&apos;ve got a little trick for that!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. get the carousel items
const carouselItems = carouselElement.children;

// 2. Set holding variables for new height of the carousel
let proposedCarouselHeight = 100;

// 3. use height of the tallest item
carouselElement.style.display = &apos;block&apos;;

carouselItemsArray.map(item =&amp;gt; {
  if(item.offsetHeight &amp;gt; proposedCarouselHeight) {
    // set the proposed height to the height of the tallest image
    proposedCarouselHeight = getHiddenElementHeight(item);
  }
});
carouselElement.style.height = `${proposedCarouselHeight}px`;

// 4. set the carousel to be visible
carouselElement.classList.add(&apos;tw-block&apos;); // or carouselElement.style.display = &apos;block&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;getHiddenElementHeight()&lt;/code&gt; function is a bit sneaky. If you do &lt;code&gt;element.offsetHeight&lt;/code&gt; on a hidden element, the value is going to be &lt;code&gt;0&lt;/code&gt;. So we need to first show the item, then grab the &lt;code&gt;offsetHeight&lt;/code&gt;, then hide it again.&lt;/p&gt;
&lt;p&gt;Whilst it seems a little redundant, it does mean we again avoid that flash where images are moving around as the JavaScript initialises. This function will likely run so quickly that users don&apos;t see it happen.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function getHiddenElementHeight(element) {
  // Save the original display style
  const originalDisplay = element.style.display;

  // Temporarily show the element
  element.style.display = &apos;block&apos;;

  // Get the height
  const height = element.offsetHeight;

  // Revert to the original display style
  element.style.display = originalDisplay;

  return height;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have a carousel with a specific pixel height, and all items inside it can be set to &lt;code&gt;position: absolute&lt;/code&gt; with &lt;code&gt;inset: 0&lt;/code&gt; to make sure they all take up the height of the container.&lt;/p&gt;
&lt;h2&gt;Autorotation Interval&lt;/h2&gt;
&lt;p&gt;The last tricky little bit was to facilitate custom autoplay speeds with only class names available.&lt;/p&gt;
&lt;p&gt;The api for this is that the user would add &lt;code&gt;carousel-autoplayspeed-&amp;lt;number&amp;gt;&lt;/code&gt; to the class list, we can then check if there&apos;s a class beginning with &lt;code&gt;carousel-autoplay-speed&lt;/code&gt;, and parse the digits at the end to get the desired speed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let autoRotationInterval = 3000;

const speedClasses = carouselElement.className.match(/carousel-autoplayspeed-\d+/g);

if (speedClasses) {
  const speedClass = speedClasses[0].split(&quot;-&quot;)[2]
  autoRotationInterval = parseInt(speedClass);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now users can specify a millisecond interval of their choosing.&lt;/p&gt;
&lt;p&gt;Here&apos;s the full implementation, including indicators and navigation buttons. Happy copypasta!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function getHiddenElementHeight(element) {
  // Save the original display style
  const originalDisplay = element.style.display;

  // Temporarily show the element
  element.style.display = &apos;block&apos;;

  // Get the height
  const height = element.offsetHeight;

  // Revert to the original display style
  element.style.display = originalDisplay;

  return height;
}
/**
 * 
 * @param {HTMLElement} carouselElement 
 * @returns a ✨ new carousel ✨
 * @notes
 * 
 * The carousel has some parameters which can be modified by adding classes to the carousel element
 * 
 * 1. Autorotation speed. Add a class of `carousel-autoplayspeed-&amp;lt;number&amp;gt;` where `&amp;lt;number&amp;gt;` is the speed in milliseconds to adjust the default rotation speed
 * 3. Show indicators. Add a class of `carousel-show-indicators` to show indicators
 * 4. Show navigators. Add a class of `carousel-show-navigators` to show navigators
 * 
 */
function createCarousel(
  carouselElement,
) {
  // nope
  if (typeof window === &apos;undefined&apos;) {
    return;
  }
  // also nope
  if (!carouselElement) {
    return;
  }
  
  /** @type { HTMLElement[] | HTMLCollection | null} */
  const carouselItems = carouselElement.children;
  
  // still nope
  if(!carouselItems) {
    console.info(`carousel doesn&apos;t seem to have any items`)
    return;
  }
  
  let autoRotationInterval = 3_000;
  
  const autoPlayClass = carouselElement.className.match(/carousel-autoplayspeed-\d+/g)
  if(autoPlayClass) {
    const autoPlaySpeed = autoPlayClass[0].split(&quot;-&quot;)[2]
    autoRotationInterval = parseInt(autoPlaySpeed);
  }

  /** @type {HTMLElement[]} */
  const carouselItemsArray = Array.from(carouselItems).filter(item =&amp;gt; item instanceof HTMLElement);

  // 1. set carousel to be full width of the containing area
  carouselElement.style.width = &apos;100%&apos;;

  // 2. Set holding variables for new width / height of carousel
  let proposedCarouselHeight = 100;

  // if not, use height of the tallest image
  carouselElement.style.display = &apos;block&apos;;
  carouselItemsArray.map(item =&amp;gt; {
    if(item.offsetHeight &amp;gt; proposedCarouselHeight) {
      // set the proposed height to the height of the tallest image
      proposedCarouselHeight = getHiddenElementHeight(item);
    }
  });
  carouselElement.style.height = `${proposedCarouselHeight}px`;

  // finally, set the carousel to be visible
  carouselElement.classList.add(&apos;tw-block&apos;, &apos;tw-relative&apos;, &apos;md:tw-overflow-hidden&apos;);

  // define interval timer so we can clear it later
  let intervalInstance = null;

  /**
   * HELPER FUNCTIONS
   */
  /**
 
   * Gets the currently active slide
   * @returns {HTMLElement | Element} item
   */
  function getActiveItem() {
    const activeItem = carouselElement?.querySelector(&apos;[data-carousel-item-current=&quot;true&quot;]&apos;);
    
    if(!activeItem || !(activeItem instanceof HTMLElement)) {
      // @ts-ignore carouselItems is definitely defined by this point
      return carouselItems[0];
    }
    return activeItem;
  }
  /**
   * 
   * gets the position of the item in the array
   * @param {HTMLElement | Element} item 
   * @returns {number} itemIndex
   */
  function getPositionOfItem(item) {
    const position = carouselItemsArray.findIndex((carouselItem) =&amp;gt; {
      return carouselItem === item &amp;amp;&amp;amp; carouselItem.getAttribute(&apos;data-carousel-item&apos;);
    });
    return position;
  }
  /**
   * 
   * Sets the carousel to the next slide
   * @param {HTMLElement | Element} carouselItem
   * @returns {void}
   */
  function setItemAsActive(carouselItem) {
    carouselItem.setAttribute(&apos;data-carousel-item-current&apos;, &apos;true&apos;);
    carouselItem.classList.remove(&apos;tw-opacity-0&apos;);
    carouselItem.classList.add(&apos;tw-opacity-100&apos;);
  }
  /**
   * 
   * @param {HTMLElement| Element } item
   * @returns null
   */
  function setItemAsInactive(item) {
    item.setAttribute(&apos;data-carousel-item-current&apos;, &apos;false&apos;);
    item.classList.add(&apos;tw-opacity-0&apos;);
    item.classList.remove(&apos;tw-opacity-100&apos;);
  }

  /**
   * ACTIONS
   */
  /**
   * Set an interval to cycle through the carousel items
   * @returns {void}
   */
  function cycle() {
    if (autoRotationInterval &amp;lt;= 0) {
      return;
    }
    intervalInstance = window.setTimeout(() =&amp;gt; {
      next();
    }, autoRotationInterval);
  }
  /**
   * Clears the cycling interval
   * @returns {void}
   */
  function pause() {
    clearInterval(intervalInstance);
  }
  /**
   * Slides to the next position
   * 
   * @param {HTMLElement | Element} nextItem 
   * @returns {void}
  */
  function slideTo(nextItem) {
    const activeItem = getActiveItem();
    if (!activeItem || !nextItem) {
      return;
    }

    setItemAsInactive(activeItem);
    setItemAsActive(nextItem);
    showActiveIndicator(nextItem);
    pause();
    cycle();
  }

  function showActiveIndicator(nextItem) {
    const nextItemIndex = getPositionOfItem(nextItem);

    const indicators = carouselElement.querySelectorAll(&apos;[data-carousel-indicator-for]&apos;);

    indicators &amp;amp;&amp;amp; Array.from(indicators).map((indicator, index) =&amp;gt; {
      if (index === nextItemIndex) {
        indicator.setAttribute(&apos;aria-pressed&apos;, &apos;true&apos;);
      } else {
        indicator.setAttribute(&apos;aria-pressed&apos;, &apos;false&apos;);
      }
    });
  }
  /**
   * Based on the currently active item it will go to the next position
   * @returns {void}
   */
  function next() {
    const activeItem = getActiveItem();
    const activeItemPosition = getPositionOfItem(activeItem) ?? 0;

    if(!carouselItems) {
      return;
    }
    if (activeItemPosition === carouselItems.length - 1) {
      // if it is the last item, set first item as next
      return slideTo(carouselItems[0]);
    }
    const nextItem = carouselItems[activeItemPosition + 1];
    
    if(!nextItem.getAttribute(&apos;data-carousel-item&apos;)) {
      // if it&apos;s an indicator, set first item as next
      return slideTo(carouselItems[0]);
    }
    slideTo(nextItem);
  }

  /**
   * Based on the currently active item it will go to the previous position
   * @returns {void}
   */
  function prev() {
    if(!carouselItems) return;
    let activeItem = getActiveItem();

    if(!activeItem) {
      console.log(&apos;no active item&apos;);
      activeItem = carouselItems[0];
    }

    const activeItemPosition = getPositionOfItem(activeItem);
    
    const prevItem = carouselItems[activeItemPosition - 1];
    
    const actualCarouselItems = carouselItemsArray.filter(item =&amp;gt; item.getAttribute(&apos;data-carousel-item&apos;));
    
    if(!prevItem &amp;amp;&amp;amp; actualCarouselItems) {
      return slideTo(actualCarouselItems[actualCarouselItems.length - 1]);
    }
    slideTo(prevItem);
  }

  /**
   * INIT FUNCTIONS
   */

  /**
  * Create the indicators for the carousel
  * @returns {void}
  */
  function createIndicators() {

    if(!carouselElement.classList.contains(&apos;carousel-show-indicators&apos;)) {
      return;
    }
    
    const indicatorContainer = `
      &amp;lt;div class=&quot;indicator-container tw-absolute tw-bottom-4 tw-left-0 tw-right-0 tw-flex tw-justify-center tw-mb-4&quot;&amp;gt;
        ${carouselItemsArray.map((item, index) =&amp;gt; `
          &amp;lt;button data-carousel-indicator-for=&quot;${index}&quot; aria-pressed=&quot;${index === 0 ? &quot;true&quot; : &quot;false&quot;}&quot; class=&quot;tw-w-4 tw-h-4 tw-mx-1 tw-rounded-full tw-border tw-border-primary-600 tw-transition-colors tw-duration-300 tw-ease-in-out tw-cursor-pointer hover:tw-ring-2 hover:tw-ring-primary-600 tw-bg-white aria-pressed:tw-bg-zinc-500&quot; aria-label=&quot;Slide ${index + 1}&quot;&amp;gt;
          &amp;lt;/button&amp;gt;
        `).join(&apos;&apos;)}
      &amp;lt;/div&amp;gt;
      `;
    
      carouselElement.insertAdjacentHTML(&apos;beforeend&apos;, indicatorContainer);

      const instantiatedIndicators = carouselElement.querySelectorAll(&apos;[data-carousel-indicator-for]&apos;);

      const instantiatedIndicatorsArray = Array.from(instantiatedIndicators);

      instantiatedIndicatorsArray.map(indicator =&amp;gt; {
        const clickedCarouselItem = indicator.getAttribute(&apos;data-carousel-indicator-for&apos;);
        indicator?.addEventListener(&apos;click&apos;, () =&amp;gt; {
          clearTimeout(intervalInstance);
          const carouselItem = carouselItemsArray.find((carouselItem) =&amp;gt; carouselItem.getAttribute(&apos;data-carousel-item&apos;) === clickedCarouselItem);
          carouselItem &amp;amp;&amp;amp; slideTo(carouselItem);
          
          instantiatedIndicators.forEach((indicator) =&amp;gt; {
            indicator.setAttribute(&apos;aria-pressed&apos;, &apos;false&apos;);
          });

          indicator.setAttribute(&apos;aria-pressed&apos;, &apos;true&apos;);
        });
      })
  }


  function createNavigators() {
    if(!carouselElement.classList.contains(&apos;carousel-show-navigators&apos;)) {
      return;
    }
    const navigatorPrev = `
      &amp;lt;button class=&quot;carousel-navigate navigate-prev tw-absolute tw-left-0 tw-bottom-0 tw-top-0 tw-text-white tw-text-2xl tw-shadow-sm tw-transition-all hover:tw-scale-110&quot; type=&quot;button&quot;&amp;gt;&amp;lt;span class=&quot;tw-block tw-rounded tw-transition-opacity tw-bg-white/20 tw-border-white/50 hover:tw-bg-zinc-400&quot;&amp;gt;&amp;amp;larr;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
    `;
    const navigatorNext = `
      &amp;lt;button class=&quot;carousel-navigate navigate-next tw-absolute tw-right-0 tw-bottom-0 tw-top-0 tw-text-white tw-text-2xl tw-shadow-sm tw-transition-all hover:tw-scale-110&quot; type=&quot;button&quot;&amp;gt;&amp;lt;span class=&quot;tw-block tw-rounded tw-transition-opacity tw-bg-white/20 tw-border-white/50 hover:tw-bg-zinc-400&quot;&amp;gt;&amp;amp;rarr;&amp;lt;/span&amp;gt;&amp;lt;/button&amp;gt;
    `;
    carouselElement.insertAdjacentHTML(&apos;beforeend&apos;, navigatorPrev);
    carouselElement.insertAdjacentHTML(&apos;beforeend&apos;, navigatorNext);

    carouselElement.querySelectorAll(&apos;.carousel-navigate&apos;)?.forEach((navigator) =&amp;gt; {
      navigator.addEventListener(&apos;click&apos;, () =&amp;gt; {
        navigator.classList.contains(&apos;navigate-prev&apos;) ? prev() : next();
      });
    });
  }

  /**
   * Function to initialise carousel
   * @returns {void}
   */
  function init() {
    const activeItem = getActiveItem();

    if(!carouselItems) {
      return;
    }

    carouselItemsArray.map((item, index) =&amp;gt; {
      item.classList.add(
        &apos;tw-absolute&apos;,
        &apos;tw-inset-0&apos;,
        &apos;tw-transition-opacity&apos;,
        &apos;tw-opacity-0&apos;,
        &apos;tw-duration-600&apos;,
      )
      item.setAttribute(&apos;data-carousel-item&apos;, `${index}`);
    });
    /**
     * if no active item is set then first position is default
    */
    if (activeItem) {
      slideTo(activeItem);
    } else {
      carouselItems &amp;amp;&amp;amp; slideTo(carouselItems[0]);
    }
    /**
     * Add event listeners to the buttons if they exist
     */
    const nextButton = carouselElement.querySelector(&apos;[data-carousel-next]&apos;);
    nextButton &amp;amp;&amp;amp; nextButton.addEventListener(&apos;click&apos;, () =&amp;gt; {
      next();
    });
    const prevButton = carouselElement.querySelector(&apos;[data-carousel-prev]&apos;);
    prevButton &amp;amp;&amp;amp; prevButton.addEventListener(&apos;click&apos;, () =&amp;gt; {
      prev();
    });
  }
  createIndicators();
  createNavigators();
  init();
  cycle();
}

// GO!
document.querySelectorAll(&apos;.carousel&apos;).forEach(carousel =&amp;gt; {
  createCarousel(carousel);
  carousel.classList.remove(&apos;tw-hidden&apos;);
})
&lt;/code&gt;&lt;/pre&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Implementing a carousel with classes only.&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Code really is Poetry</title><link>https://deliciousreverie.co.uk/posts/code-is-poetry/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/code-is-poetry/</guid><description>I&apos;m not sure if you remember the early days of the web. I can. I remember when my friend and I would sit at home, bored of talking about Babylon 5 (yes, really) and yet not wanting to part company again.</description><pubDate>Tue, 02 Jun 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m not sure if you remember the early days of the web. I can. I remember when my friend and I would sit at home, bored of talking about Babylon 5 (yes, really) and yet not wanting to part company again.&lt;/p&gt;
&lt;p&gt;&quot;Let&apos;s try to get on the internet!&quot; I would excitedly yelp, and plug in the 14.4k modem, dial into Apple&apos;s &lt;a href=&quot;https://en.wikipedia.org/wiki/EWorld&quot;&gt;eWorld&lt;/a&gt; online community, and download an early version of Mosaic or Netscape.&lt;/p&gt;
&lt;p&gt;It never worked.&lt;/p&gt;
&lt;p&gt;Mostly because my mum kept noticing the £400 extra on the telephone bill each time we tried. So we stuck to writing science fiction stories and, after a brush with classics (via an old copy of This Side of Paradise by F Scott Fitzgerald), I started to get into poetry.&lt;/p&gt;
&lt;p&gt;Poetry represented for me the biggest leap in my adolescent life so far. My interests went from material that could be taken at face-value, or so was my perspective at the time, to deeper things with hidden meaning.&lt;/p&gt;
&lt;p&gt;Every time I tried to comprehend a poem it seemed to change depending on my mood, on how I read each word and stanza. I was genuinely moved by things I could barely understand ... the underpinning structure and esoteric references being quite alien to my understanding.&lt;/p&gt;
&lt;h3&gt;The Poets Write HTML &lt;a href=&quot;https://deliciousreverie.co.uk/posts/code-is-poetry/#the-poets-write-html&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;m not sure if you&apos;ll remember also that many of the first users of the web were writers, if not poets?&lt;/p&gt;
&lt;p&gt;For them, the internet represented freedom ... absolute freedom to be published no matter the quality of their writing, no matter the popularity or availability (or distinct lack thereof) of willing publishers. A writer could reach an audience of millions just by learning to write a few lines of code.&lt;/p&gt;
&lt;p&gt;But code was pretty hard to grapple with. One poem titled &quot;The Poet Writes HTML&quot; published in Cardiff&apos;s &lt;a href=&quot;https://www.poetrymagazines.org.uk/magazine/index.asp?id=72&quot;&gt;The Yellow Crane&lt;/a&gt; told how the author felt he was getting further and further away from writing because of the time he spent learning to code. For many years, that account alone turned me away from technology altogether.&lt;/p&gt;
&lt;p&gt;But when I came back ... that was when a new revelation hit me.&lt;/p&gt;
&lt;h3&gt;The Language of the Internet &lt;a href=&quot;https://deliciousreverie.co.uk/posts/code-is-poetry/#the-language-of-the-internet&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At once, there was a new world of hidden meaning and esoteric references, whose structure I was only beginning to comprehend. Code could be controlled, tamed, be bent to your will. It could be made to sit, roll over and beg like a dog. I found it truly is a language with all that implies: a language made up of words with meanings that once again had weight and mystery and consequences.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Code really is Poetry&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Coder Career: Ben Read on Becoming a Senior Engineer</title><link>https://deliciousreverie.co.uk/posts/coder-career-ben-read-on-becoming-senior-engineer/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/coder-career-ben-read-on-becoming-senior-engineer/</guid><description>Ben Read is a Senior Software Engineer at PurpleBricks, specializing in JavaScript development. Ben joins TCC to discuss his journey from junior to senior, accidentally starting a web development agency, and the early days of the internet.</description><pubDate>Fri, 01 Oct 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Ben Read is a Senior Software Engineer at PurpleBricks, specializing in JavaScript development. Ben joins TCC to discuss his journey from junior to senior, accidentally starting a web development agency, and the early days of the internet.&lt;/p&gt;
&lt;p&gt;I had a lot of fun with my personal friend Cameron on this podcast episode.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://open.spotify.com/episode/2t0NwCXJsHue1mph7aX8UE?si=30b642b1ac5f4335&quot;&gt;Listen to it here&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Coder Career: Ben Read on Becoming a Senior Engineer&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Building a Multitenant Kubernetes System</title><link>https://deliciousreverie.co.uk/posts/building-multitenant-kubernetes-with-capsule/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/building-multitenant-kubernetes-with-capsule/</guid><description>I recently decided to delve a little deeper into Kubernetes by building my own multitenant platform using Capsule.</description><pubDate>Sun, 27 Oct 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I recently decided to delve a little deeper into Kubernetes by building my own multitenant platform using Capsule.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This isn&apos;t meant as a tutorial really, I just want to document my learning so far.&lt;/p&gt;
&lt;p&gt;Firstly, I must say that my explorations of Kubernetes and friends would have been a lot more challenging if if hadn&apos;t been for &lt;a href=&quot;https://www.warp.dev/&quot;&gt;Warp&lt;/a&gt;, a terminal emulator that has great multiplexing and a built-in AI trained on the publicly available docs of relevant tools.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I set myself the following key requirements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;To utilise baremetal or virtual servers, not tied to one service or hosting company&lt;/li&gt;
&lt;li&gt;To isolate each environment so they can&apos;t accidentally visit each others&apos; sites&lt;/li&gt;
&lt;li&gt;To facilitate easy creation, deletion and &quot;pausing&quot; of the environment&lt;/li&gt;
&lt;li&gt;To use a custom domain for each&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That last one is a bit more of a challenge since I am not all that familiar with load balancers yet, but we&apos;ll get there...&lt;/p&gt;
&lt;h2&gt;Reproducability&lt;/h2&gt;
&lt;p&gt;I had my own set of requirements to fulfil alongside these that I thought would help, most of these revolve around reproducability.&lt;/p&gt;
&lt;p&gt;I wanted to be able to destroy and spin up new environments easily. That meant using the Helm package manager, &lt;a href=&quot;https://helmfile.readthedocs.io/en/latest/&quot;&gt;Helmfile&lt;/a&gt;. With this tool I can declare the system I want, which would also help to roll the system back to a previous state more easily.&lt;/p&gt;
&lt;p&gt;I also wanted the environment to be built with Infrastructure-as-code. A machine can easily become corrupted or attacked. For these reasons, I am much more inclined towards thinking about them as disposable tools than precious investments.&lt;/p&gt;
&lt;p&gt;So here&apos;s the Helmfile I cam up with (with a little help from Warp):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;repositories:
  - name: clastix
    url: https://clastix.github.io/charts
  
releases:
  - name: capsule
    namespace: capsule-system
    chart: clastix/capsule
    version: 0.4.6
    values:
      - capsule:
          config:
            forceTenantPrefix: false
            protectedNamespaceRegex: &quot;^(kube-system|kube-public|kube-node-lease|kube-kube-.*|default|capsule-system)$&quot;
            protectedNamespaceLabels: []
            enableTLS: true
            logLevel: info
          ingress:
            enabled: false
            hostName: capsule.local
            annotations: {}
            tls:
              enabled: true
              secretName: &quot;&quot;

        resources:
          requests:
            cpu: &quot;100m&quot;
            memory: &quot;128Mi&quot;
          limits:
            cpu: &quot;200m&quot;
            memory: &quot;256Mi&quot;

          nodeSelector: {}
          tolerations: []

          additionalLabels: {}
          additionalAnnotations: {}

          repositories:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had a voucher from Linode (Linode is quickly being rebranded as Akamai Connected Cloud), which I was grateful for since that helped me get familiar with the difference between nodes and pods: nodes are external resources such as my Linode VMs, pods are internal resources. The number of nodes you have depends on how many VMs you have (on Linode at least, one will be a controller node from which to manage the others).&lt;/p&gt;
&lt;p&gt;However I was afraid of running out of trial credits, so I also deployed this environment locally using Minikube. I have to say, this is a great tool for trialling software locally. It deploys it&apos;s own VM (which shows up in VirtManager on Linux), and it also creates the Kubeconfig for you automatically.&lt;/p&gt;
&lt;p&gt;So I guess I&apos;d better explain Kubeconfig.&lt;/p&gt;
&lt;h2&gt;How to manage Kubernetes&lt;/h2&gt;
&lt;p&gt;Kubernetes is the thing that manages your pods. To manage Kubernetes, you need Kubeconfig, a command-line tool that you can use to authenticate yourself and apply configuration to your Kubernetes &lt;em&gt;namespace&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;A namespace is a group of resources. In my system, the main &lt;em&gt;namespace&lt;/em&gt; is minikube, then there&apos;s my Capsule namespace which manages resources for my multitenant system. And of course, each of those tenants has it&apos;s own namespace too.&lt;/p&gt;
&lt;p&gt;You can find out what namespaces you have by running the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl get namespaces
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each time you want to change a resource you have to load the correct namespace, this is usually a switch or flag on the command line like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl apply -f ./values.yaml --namespace=capsule-system

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl apply -f ./values.yaml -n capsule-system
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is telling Kubectl to apply the values from the file (&lt;code&gt;-f&lt;/code&gt;) that follows to the namespace (&lt;code&gt;-n&lt;/code&gt;) that follows.&lt;/p&gt;
&lt;p&gt;As you can imagine this gets a little confusing because you can easily forget which namespace you&apos;re in. Especially when I was setting up my first tenant and was trying to run two kubectl configurations in one terminal session. Yeah don&apos;t try that.&lt;/p&gt;
&lt;p&gt;To find out what user you are you can run&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl config view --minify
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Capsule system&lt;/h2&gt;
&lt;p&gt;What I liked about Capsule is that it has strict namespacing. At first I tried to call the Capsule system something else, and that got me in a tangled mess. It seems you need to call it &quot;capsule-system&quot; or else it doesn&apos;t work. But that helped me because I could more easily see the distinction between other namespaces (I also tried to spin up a Kubernetes dashboard).&lt;/p&gt;
&lt;p&gt;It also, like Kubernetes does natively, uses RBAC (role-based access control). This is similar to what you see in AWS: you need to create a user first, then assign them a role, and the permissions they need live inside that role, not with the actual user.&lt;/p&gt;
&lt;p&gt;This is a useful abstraction because if your user is compromised you can withdraw their permissions which will preserve the resources and the permissions they need to function.&lt;/p&gt;
&lt;p&gt;But it does take some getting used to. If your kubectl is set to use a tenant, they won&apos;t be able to do a lot unless you first load the capsule system user which has all the necessary permissions to grant those to your users.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl get rolebindings,clusterrolebindings --all-namespaces -o wide
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful for finding out what permissions each user has.&lt;/p&gt;
&lt;p&gt;You can imagine this can get a bit frustrating, especially if you&apos;re just trying to work this out.&lt;/p&gt;
&lt;p&gt;I like to drop myself in the deep end like that.&lt;/p&gt;
&lt;p&gt;However I did get more than a little frustrated that this whole thing is only &lt;em&gt;partially&lt;/em&gt; declarative.&lt;/p&gt;
&lt;p&gt;What I mean by that is that you have to perform the following steps to do anything significant:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alter your YAML files to modify permissions&lt;/li&gt;
&lt;li&gt;Apply the config&lt;/li&gt;
&lt;li&gt;Check it worked&lt;/li&gt;
&lt;li&gt;Undo your alterations if it didn&apos;t&lt;/li&gt;
&lt;li&gt;Apply the config to roll it back&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This gets tedious and more than once I forgot to un-apply my changes and therefore had to unpick things a few times.&lt;/p&gt;
&lt;p&gt;For me it was like having committed to a remote Git branch and then only being able to see the effects on a hosted environment. It&apos;s a rather wide feedback loop that I want to shorten.&lt;/p&gt;
&lt;p&gt;There&apos;s not a lot of documentation for multi-tenancy systems. I guess if you&apos;re building one you&apos;re not exactly going to shout it from the rooftops.&lt;/p&gt;
&lt;h2&gt;Another approach&lt;/h2&gt;
&lt;p&gt;I did &lt;a href=&quot;https://www.pulumi.com/ai/answers/xszk8bipK4VH383j6ksP2C/multi-tenancy-with-kuma-in-kubernetes&quot;&gt;find this documentation from another IAC platform I really like, Pulumi&lt;/a&gt;. It&apos;s been AI generated but might be a good starting point. I like the fact that I can programatically declare my namespace and tenants in one file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import * as k8s from &quot;@pulumi/kubernetes&quot;;

// Create a Kubernetes cluster using the preferred cloud provider.
// This is an abstract example; specifics would depend on the cloud provider in use.
const cluster = new k8s.Cluster(&quot;multi-tenant-cluster&quot;, {
    // Configure the cluster settings here.
    // For example, on AWS you would set the version and node type,
    // and the Pulumi AWSX library would provision an EKS cluster for you.
});

// Configure Kubernetes provider to use the generated kubeconfig from the cluster above.
const provider = new k8s.Provider(&quot;k8s-provider&quot;, {
    kubeconfig: cluster.kubeconfig,
});

// Create namespaces for each tenant.
const tenantA = new k8s.core.v1.Namespace(&quot;tenant-a&quot;, {}, { provider });
const tenantB = new k8s.core.v1.Namespace(&quot;tenant-b&quot;, {}, { provider });

// Now we might apply a Kuma installation to our cluster
// Note: Specifics would vary based on your use case and would likely involve
// custom configurations, which are beyond the scope of this program.
// We assume that we have a definition file `kuma-control-plane.yaml` that contains
// the resources to set up Kuma, including a Namespace, Deployments, Services, etc.
const kuma = new k8s.yaml.ConfigGroup(&quot;kuma&quot;, {
    files: [&quot;kuma-control-plane.yaml&quot;],
}, { provider });

// Export the kubeconfig to access your cluster.
export const kubeconfig = cluster.kubeconfig;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And yes, that&apos;s infrastructure as code with TypeScript. A significant abstraction but one I can get behind.&lt;/p&gt;
&lt;p&gt;This is far from complete of course, there&apos;s a long list of things to set up besides this, which since there&apos;s no copyright on AI generated docs I can paste here directly in case it&apos;s taken down:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define a ClusterRole and associated ClusterRoleBinding (or RoleBinding in each namespace) for each tenant.&lt;/li&gt;
&lt;li&gt;Set up network policies to restrict traffic flow between the tenants.&lt;/li&gt;
&lt;li&gt;Install Kuma following its documentation, tailoring the setup to your cluster&apos;s network configuration and the permissions required.&lt;/li&gt;
&lt;li&gt;Ensure that your Kuma setup works with your multi-tenancy setup, e.g., by making sure that Kuma&apos;s control plane respects namespace boundaries and RBAC rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I think I&apos;m going to try this again next time the kids are in the pool.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;I&apos;ve got a long way to go still: I want to automate the creation of a very specific application setup for my tenants, and each of them will need to have an ingress, I know nothing about how load balancers work yet so I&apos;m going to have to figure that out.&lt;/p&gt;
&lt;p&gt;But this has been an enjoyable learning exercise.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Building a Multitenant Kubernetes System&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Advanced Custom Fields and Bootstrap Tabs</title><link>https://deliciousreverie.co.uk/posts/advanced-custom-fields-bootstrap-tabs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/advanced-custom-fields-bootstrap-tabs/</guid><description>I&apos;m not a huge fan of Advanced Custom Fields, but there was a requirement to use it in a recent project that had Bootstrap as a basis for the UI. The challenge for me was to get Bootstrap nav-tabs to play nice with an ACF repeater field.</description><pubDate>Tue, 02 Jun 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m not a huge fan of Advanced Custom Fields, but there was a requirement to use it in a recent project that had Bootstrap as a basis for the UI. The challenge for me was to get Bootstrap &lt;a href=&quot;https://getbootstrap.com/components/#nav-tabs&quot;&gt;nav-tabs&lt;/a&gt; to play nice with an &lt;a href=&quot;https://www.advancedcustomfields.com/img/querying-the-database-for-repeater-sub-field-values/&quot;&gt;ACF repeater field&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I started with the basic HTML markup for Bootstrap&apos;s Nav Tabs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul class=&quot;nav nav-tabs&quot;&amp;gt;
  &amp;lt;li role=&quot;presentation&quot; class=&quot;active&quot;&amp;gt;&amp;lt;a href=&quot;tabone&quot;&amp;gt;TabOne&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li role=&quot;presentation&quot;&amp;gt;&amp;lt;a href=&quot;tabtwo&quot;&amp;gt;TabTwo&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;li role=&quot;presentation&quot;&amp;gt;&amp;lt;a href=&quot;tabthree&quot;&amp;gt;TabThree&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;div class=&quot;tab-content&quot;&amp;gt;
  &amp;lt;div class=&quot;tab-pane active&quot; id=&quot;tabone&quot;&amp;gt;
     Some content in tab one
&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;tab-pane active&quot; id=&quot;tabtwo&quot;&amp;gt;
     Some content in tab two
&amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;tab-pane active&quot; id=&quot;tabthree&quot;&amp;gt;
     Some content in tab three
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the Field Groups settings, I created a Repeater (this is a paid-for add on to the standard Advanced Custom Fields) called &quot;tab Panes&quot;, with 2 sub-fields, &quot;Tab Title&quot; and &quot;Tab Contents&quot;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
&amp;lt;!-- Check for parent repeater row --&amp;gt;
&amp;lt;?php if( have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
  &amp;lt;ul class=&quot;nav nav-tabs&quot; role=&quot;tablist&quot;&amp;gt;
  &amp;lt;?php // Step 1: Loop through rows, first displaying tab titles in a list
   while( have_rows(&apos;tab_panes&apos;) ): the_row();
?&amp;gt;
    &amp;lt;li role=&quot;presentation&quot; class=&quot;active&quot;&amp;gt;
      &amp;lt;a
        href=&quot;#tabone&quot;
        role=&quot;tab&quot;
        data-toggle=&quot;tab&quot;
        &amp;gt;
      &amp;lt;?php the_sub_field(&apos;tab_title&apos;); ?&amp;gt;
      &amp;lt;/a&amp;gt;
    &amp;lt;/li&amp;gt;
    &amp;lt;?php endwhile; // end of (have_rows(&apos;tab_panes&apos;) ):?&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;?php endif; // end of (have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The PHP above displays the tabs. The code below, very similarly, displays the tab panes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php if( have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
  &amp;lt;div class=&quot;tab-content&quot;&amp;gt;
  &amp;lt;?php// number rows ?&amp;gt;
  &amp;lt;?php // Step 2: Loop through rows, now displaying tab contents
   while( have_rows(&apos;tab_panes&apos;) ): the_row();
  // Display each item as a list ?&amp;gt;
      &amp;lt;div class=&quot;tab-pane active&quot; id=&quot;tabone&quot;&amp;gt;
          &amp;lt;?php the_sub_field(&apos;tab_contents&apos;); ?&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;?php endwhile; // (have_rows(&apos;tab_panes&apos;) ):?&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;?php endif; // (have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By looping through the same repeater, we can get all the tabs out of the database, no problem. But we still have two problems: 1) linking the tab to the pane 2) Assigning the class of &quot;active&quot; so the Javascript is able to add and remove the CSS to reveal / hide the appropriate pane.&lt;/p&gt;
&lt;h2&gt;1. Linking to the Pane&lt;/h2&gt;
&lt;p&gt;There are a number of ways to do this. I could ask the user to input a number to uniquely identify the tab pane. But that would add extra work to the users flow, and they might easily find themselves out of their depth. I want to make this as easy as possible for the user.&lt;/p&gt;
&lt;p&gt;On the other hand, Wordpress has a very useful function called Sanitize HTML, which we input the value of the title, take out spaces and capitals, and use this as the link:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;#&amp;lt;?php echo sanitize_html_class( the_sub_field( &apos;tab_title&apos; ) ); ?&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Assigning the &apos;Active&apos; Class&lt;/h2&gt;
&lt;p&gt;So now we need to get a class of &apos;active&apos; only on the first tab. The Bootstrap Javascript will do the rest for us. How do we do that?&lt;/p&gt;
&lt;p&gt;I added this code just inside the while loop, inside the ul tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php $row = 1; // number rows ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This php is a counter. So we can identify the first instance and assign an if statement to it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a class=&quot;&amp;lt;?php if($row == 1) {echo &apos;active&apos;;}?&amp;gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The final thing to do, is to keep the counter running, but adding this just before the endwhile.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php $row++; endwhile; // (have_rows(&apos;tab_panes&apos;) ):?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you&apos;ve added these to the tab panes in a similar way, you&apos;ll be up and running with Boostrap Tabs.&lt;/p&gt;
&lt;p&gt;Full code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php if( have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
  &amp;lt;div class=&quot;tab-content&quot;&amp;gt;
  &amp;lt;?php // Step 2: Loop through rows, now displaying tab contents
   while( have_rows(&apos;tab_panes&apos;) ): the_row();
   $row = 1; // number rows ?&amp;gt;
  // Display each item as a list ?&amp;gt;
      &amp;lt;div class=&quot;tab-pane &amp;lt;?php if($row == 1) {echo &apos;active&apos;;}?&amp;gt;&quot; id=&quot;tab&quot;&amp;gt;
          &amp;lt;?php the_sub_field(&apos;tab_contents&apos;); ?&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;?php $row++; endwhile; // (have_rows(&apos;tab_panes&apos;) ):?&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;?php endif; // (have_rows(&apos;tab_panes&apos;) ): ?&amp;gt;
```&quot;&lt;/code&gt;&lt;/pre&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Advanced Custom Fields and Bootstrap Tabs&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A Collection of Tailwind UI Loading States</title><link>https://deliciousreverie.co.uk/posts/collection-tailwind-ui-loading-states/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/collection-tailwind-ui-loading-states/</guid><description>I made a collection of loading states for Tailwind. It was relatively easy exercise, and I&apos;m surprised I didn&apos;t find anything like it out there already.</description><pubDate>Wed, 04 Oct 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I made a collection of loading states for Tailwind. It was relatively easy exercise, and I&apos;m surprised I didn&apos;t find anything like it out there already.&lt;/p&gt;
&lt;p&gt;You might recognise the first element from the &lt;a href=&quot;https://tailwindcss.com/docs/animation#pulse&quot;&gt;Tailwind Docs&lt;/a&gt;, which demonstrates how to use the &lt;code&gt;animate-pulse&lt;/code&gt; class to make it look like it&apos;s in the process of loading in.&lt;/p&gt;
&lt;p&gt;Here&apos;s the CodePen:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;eYbPBjE&quot; data-user=&quot;endymion1818&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&amp;gt;
&amp;lt;span&amp;gt;See the Pen &amp;lt;a href=&quot;https://codepen.io/endymion1818/pen/eYbPBjE&quot;&amp;gt;
Tailwind Loading UI Elements&amp;lt;/a&amp;gt; by Ben Read (&amp;lt;a href=&quot;https://codepen.io/endymion1818&quot;&amp;gt;@endymion1818&amp;lt;/a&amp;gt;)
on &amp;lt;a href=&quot;https://codepen.io&quot;&amp;gt;CodePen&amp;lt;/a&amp;gt;.&amp;lt;/span&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;Feel free to copy or remix this pen, I&apos;m not precious about it and just wanted a collection I could reference for some other work I was doing.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A Collection of Tailwind UI Loading States&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Comments are now live</title><link>https://deliciousreverie.co.uk/posts/comments-are-live/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/comments-are-live/</guid><description>A system to allow visitors to comment on my blog posts has just gone live.</description><pubDate>Thu, 21 Mar 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;A system to allow visitors to comment on my blog posts has just gone live.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It&apos;s amazing to me how quickly the internet is being eroded. It&apos;s been under way for years, but with generative AI technology, it seems to have accelerated beyond anyone&apos;s ability or desire to fix it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.wheresyoured.at/are-we-watching-the-internet-die/&quot;&gt;Are We Watching the Internet Die?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It increasingly looks like there will be two internets out there in future: the Big Tech internet, where AI generated content is indistinguishable from that made by humans. It&apos;s a homogenous place where ad tech and cookie banners flourish.&lt;/p&gt;
&lt;p&gt;The other is a return to personal websites, webrings, manually curated directories, and ... just generally the web we almost lost.&lt;/p&gt;
&lt;p&gt;And the ever increasing mass of AI generated goop? They can stay outside. We don&apos;t serve their kind here.&lt;/p&gt;
&lt;p&gt;I don&apos;t feel bad about this. It&apos;s kind of exciting to wake up and find the landscape around you quite changed.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;April is the cruellest month, breeding
Lilacs out of the dead land, mixing
Memory and desire, stirring
Dull roots with spring rain.

- TS Eliot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;But anyway, in eager anticipation of a revival of sorts, I&apos;ve added a comments section to my website.&lt;/p&gt;
&lt;p&gt;Now I&apos;ve got that, I&apos;m planning to add a guestbook and to join more directories and webrings.&lt;/p&gt;
&lt;p&gt;So, please do leave me a comment! You don&apos;t need to be logged in, and once I approve and rebuild the site, it&apos;ll be live for all to see albeit without any attributing email address.&lt;/p&gt;
&lt;p&gt;Good morning. It&apos;s a brand new day out there.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;PS If you&apos;re looking for some of those directories, here are 3 I have found useful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://blogroll.org/&lt;/li&gt;
&lt;li&gt;https://indieseek.xyz/&lt;/li&gt;
&lt;li&gt;https://ooh.directory/&lt;/li&gt;
&lt;/ul&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Comments are now live&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Comparing two redux setups</title><link>https://deliciousreverie.co.uk/posts/comparing-redux-setup/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/comparing-redux-setup/</guid><description>I recently came across two different examples of setup that used Redux to manage state in a sample app. They were so different that I thought it was worth delving a little deeper into why, and how we can write Redux code so it&apos;s a good fit for our needs. </description><pubDate>Mon, 03 Aug 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I recently came across two different examples of setup that used Redux to manage state in a sample app. They were so different that I thought it was worth delving a little deeper into why, and how we can write Redux code so it&apos;s a good fit for our needs.&lt;/p&gt;
&lt;h2&gt;Minimum Viable Redux &lt;a href=&quot;https://deliciousreverie.co.uk/posts/comparing-redux-setup/#minimum-viable-redux&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Redux used to be pretty much the only way of managing state in React without &quot;prop drilling&quot; or mutating state (which means changing it so that the original state is lost) as you go. There are a bunch of different options now, but Redux is still in common use. However that use doesn&apos;t always have a common pattern. Take this example I found on a JSFiddle by Caner Dagli:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Provider, connect } from &quot;react-redux&quot;;
import { createStore, applyMiddleware } from &quot;redux&quot;;
import thunk from &quot;redux-thunk&quot;;

function fetchPostsRequest() {
  return {
    type: &quot;FETCH_REQUEST&quot;,
  };
}

function fetchPostsSuccess(payload) {
  return {
    type: &quot;FETCH_SUCCESS&quot;,
    payload,
  };
}

function fetchPostsError() {
  return {
    type: &quot;FETCH_ERROR&quot;,
  };
}

const reducer = (state = {}, action) =&amp;gt; {
  switch (action.type) {
    case &quot;FETCH_REQUEST&quot;:
      return state;
    case &quot;FETCH_SUCCESS&quot;:
      return { ...state, posts: action.payload };
    default:
      return state;
  }
};

function fetchPostsWithRedux() {
  return (dispatch) =&amp;gt; {
    dispatch(fetchPostsRequest());
    return fetchPosts().then(([response, json]) =&amp;gt; {
      if (response.status === 200) {
        dispatch(fetchPostsSuccess(json));
      } else {
        dispatch(fetchPostsError());
      }
    });
  };
}

function fetchPosts() {
  const URL = &quot;https://jsonplaceholder.typicode.com/posts&quot;;
  return fetch(URL, { method: &quot;GET&quot; }).then((response) =&amp;gt;
    Promise.all([response, response.json()])
  );
}

class App extends React.Component {
  componentDidMount() {
    this.props.fetchPostsWithRedux();
  }
  render() {
    return (
      &amp;lt;ul&amp;gt;
        {this.props.posts &amp;amp;&amp;amp;
          this.props.posts.map((post) =&amp;gt; {
            return &amp;lt;li&amp;gt;{post.title}&amp;lt;/li&amp;gt;;
          })}
      &amp;lt;/ul&amp;gt;
    );
  }
}

function mapStateToProps(state) {
  return {
    posts: state.posts,
  };
}

let Container = connect(mapStateToProps, { fetchPostsWithRedux })(App);

const store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
  &amp;lt;Provider store={store}&amp;gt;
    &amp;lt;Container /&amp;gt;
  &amp;lt;/Provider&amp;gt;,
  document.getElementById(&quot;container&quot;)
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I really think this example is great, it shows us how to write the most basic boilerplate code for a Redux store and how you might use it for a simple use case, one data type and a single action.&lt;/p&gt;
&lt;p&gt;So does Redux require a lot of boilerplate code? Not in this example. However, if you want to be able to scale it to use different data types (think posts and users and tags and pages ...), the above code isn&apos;t going to scale.&lt;/p&gt;
&lt;h2&gt;Redux at scale &lt;a href=&quot;https://deliciousreverie.co.uk/posts/comparing-redux-setup/#redux-at-scale&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Take a look at this example, it&apos;s a GatsbyJS starter but I hope you can still find your way around the code: &lt;a href=&quot;https://github.com/Evaluates2/Gatsby-Starter-TypeScript-Redux-TDD-BDD&quot;&gt;https://github.com/Evaluates2/Gatsby-Starter-TypeScript-Redux-TDD-BDD&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you peek inside the /src/ folder, you&apos;ll see another folder, /state/, with another 4 folders, /actions/, /middlewares/, /reducers/ and /types/.&lt;/p&gt;
&lt;p&gt;Just look inside the /actions/ folder and you&apos;ll see four files. Two are tests, but there&apos;s an action for each data type (login and todos respectively). Similarly, there&apos;s a reduce for each data type, middleware for each type ... etc.&lt;/p&gt;
&lt;p&gt;This code is built for extensibility. By compartmentalising code like this I can see that I will be able to add another type (like lists or pages or transactions) without having to make fundamental changes to how this project is set up. Smiliarly, if something changed in my todos, I can see more easily where I need to go to change something so that the app still renders in the way I expect it to.&lt;/p&gt;
&lt;p&gt;I liked this example because it shows us a totally different way of writing Redux code. It helps us see the separate concerns we need to care about (reducers, actions, middleware even) if we are going to write a robust application that will be able to serve many product iterations over a longer period of time without major refactors.&lt;/p&gt;
&lt;p&gt;I think these two approaches also teach us that Redux is a powerful suite of tools that help us manage state in our applications — and that it can be used in different ways depending on your desired approach.&lt;/p&gt;
&lt;p&gt;But they also tell us something about software development: the fastest approach is great for some small project you&apos;re only going to touch once. But architecting an application that will withstand the test of time is a different game altogether.&lt;/p&gt;
&lt;p&gt;That&apos;s the great thing about the way Redux is built: it doesn&apos;t get in the way of writing new code ... providing we start out in the right way.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Comparing two redux setups&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A congratulatory message with hidden meaning</title><link>https://deliciousreverie.co.uk/posts/congratulatory-message-hidden-meaning/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/congratulatory-message-hidden-meaning/</guid><description>I&apos;m particularly proud of this piece of artwork and messaging I came up with for the Webiny community, not only because of the message it conveys to our community, but also because it also has a hidden message.</description><pubDate>Wed, 30 Nov 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;One of the objectives I set out to achieve at Webiny was to increase the positivity among members of our audience in the hopes that they would become more vocal participants of the community and join in a bit more with our discussions.&lt;/p&gt;
&lt;p&gt;To achieve that I wanted to create messaging that would be shared primarily on the company newsletter to emphasize how much we valued our community members and to thank them for being there.&lt;/p&gt;
&lt;p&gt;This is the artwork I came up with:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I really liked how this turned out for a few reasons: it was a clear message and wasn&apos;t too cluttered and it only too me a few minutes to put together. But I liked it for other reasons too.&lt;/p&gt;
&lt;p&gt;Although the primary message in the emoji icons says &quot;you&quot; and &quot;awesome&quot;, the message is coming from us (ie. inside the screen), so in fact we are identifying ourselves as the &quot;superhero&quot; pointing out towards the audience. The subliminal message is that we are awesome because we&apos;re enabling you.&lt;/p&gt;
&lt;p&gt;Also by featuring the &quot;Webiny&quot; logo behind the superhero figure, we are further emphasizing that the power to be awesome comes from Webiny.&lt;/p&gt;
&lt;p&gt;This artwork was sent out a few months ago as the last segment of our monthly email newsletter. I particularly wanted to feature it at the end of the newsletter as an unexpected twist and a pleasant surprise for anyone who got that far through the newsletter. I don&apos;t know if anyone caught onto this messaging but that&apos;s not the point; by continually sharing such positive thoughts, positivity towards Webiny will inevitably result, and the result of that is organic growth.&lt;/p&gt;
&lt;p&gt;I thoroughly enjoy making these little surprises that reward people that notice and make them feel valued.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A congratulatory message with hidden meaning&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Content Authoring with StrapiJs</title><link>https://deliciousreverie.co.uk/posts/content-authoring-with-strapijs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/content-authoring-with-strapijs/</guid><description>In order for people outside of the development team or community to use your software, care must be given to allow them to write and publish content with the least amount of friction or obstruction as possible. A well crafted interface that allows people to do this isn&apos;t easy. I have started to use Strapi as a serverless backend API interface recently and believe it&apos;s going to be a very useful solution for a lot of projects. </description><pubDate>Sat, 18 Aug 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For years, WordPress has been the de facto standard for content authoring on the web. The reasons for this are many, but principal among them is it&apos;s clean, easy-to-use interface that gives content authors control over nearly every aspect of their content. WordPress isn&apos;t going away, but hosting and maintaining it safely can be a challenge.&lt;/p&gt;
&lt;p&gt;At the organisation I work for, we have a WordPress blog, but almost no PHP expertise as we were moving from Ruby towards a JavaScript stack. We also have another service that used a WordPress-like interface, but were tied into expensive hosted services - which was deprecated, and yet still fulfilled an essential function as a core part of our content strategy.&lt;/p&gt;
&lt;p&gt;For this reason, I started to investigate a way of amalgamating this content into one location, on one platform (preferably JavaScript), with an API so that I could pull in content to our static site built with Gatsbyjs.&lt;/p&gt;
&lt;h2&gt;The Elusive Dashboard &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#the-elusive-dashboard&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are lots of services out there that could have fulfilled our requirements, but all of these stored content on another platform, an approach we patently wanted to avoid. For that reason, services like &lt;a href=&quot;https://forestry.io/&quot;&gt;Forestry&lt;/a&gt;, &lt;a href=&quot;https://www.contentful.com/&quot;&gt;Contentful&lt;/a&gt;, and even &lt;a href=&quot;https://www.sanity.io/&quot;&gt;Sanity&lt;/a&gt; with its installable api and hosted backend, were out of the equation.&lt;/p&gt;
&lt;p&gt;Instead I looked into dashboard / content authoring UIs that we could build an API around. I saw that &lt;a href=&quot;https://github.com/yogiben/meteor-admin&quot;&gt;Meteorjs had an open-source project in this vane&lt;/a&gt;, but that there really weren&apos;t that many active projects on NPM that we could utilise.&lt;/p&gt;
&lt;p&gt;Then I discovered Strapi.&lt;/p&gt;
&lt;h2&gt;A Content Interface &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#a-content-interface&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Strapi quickly started looking like it was a product that could fit our requirements. A self-hosted, API-focused system with a well designed dashboard and a useable interface. I particularly liked that we could create content types on the frontend, designate the fields, and start using them straight away.&lt;/p&gt;
&lt;p&gt;Strapi showed promise, however version 1 was depracated, v2 hadn&apos;t made it out of the door, and v3 was still in Alpha release stage. This is a cause for concern, however the team were progressing with a good deal of focus on fulfilling key user needs. I decided I would get involved in the project and start using it for personal work.&lt;/p&gt;
&lt;h2&gt;Deploying Strapi on Heroku &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#deploying-strapi-on-heroku&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since my focus over the past few years has been Severless technology, I wanted to deploy Strapi on &lt;a href=&quot;https://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;, a managed platform with a generous free tier that allows you to host applications in a raft of different languages, from PHP and Ruby, to Go and JavaScript.&lt;/p&gt;
&lt;p&gt;Fortunately for me, there&apos;s already a decent &lt;a href=&quot;https://github.com/strapi/strapi/&quot;&gt;Strapi starter for Heroku&lt;/a&gt;, which is advertised right on the Strapi repo readme. One click and I had started my own Heroku instance, using &lt;a href=&quot;https://mlab.com/&quot;&gt;mLab&lt;/a&gt; to host the MongoDB database, all connected up and ready to be used.&lt;/p&gt;
&lt;p&gt;This is great for giving Strapi a spin, but I wanted this to be my content platform. To do that I needed to create my own fields and add some new content types. I added some, only to find they&apos;d rolled back again afterwards. I tried uploading images, to find they disappeared after a few hours. What was happening?!&lt;/p&gt;
&lt;p&gt;What I hadn&apos;t realised is that Heroku&apos;s file system is ephermeral, which means data stored there won&apos;t persist. You application&apos;s instance can be destroyed and rebuilt from the git store depending on Heroku&apos;s, or your, needs. So making changes to the API, storing images locally, or even data, needs to be done differently.&lt;/p&gt;
&lt;p&gt;The core issue was that I needed to clone the site locally, add my changes, then to Heroku&apos;s Git repo, and push it to Heroku&apos;s repo.&lt;/p&gt;
&lt;h2&gt;Cloning Locally &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#cloning-locally&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Cloning the Heroku build pack so you can work on it locally is a bit tricky. I &lt;a href=&quot;https://help.heroku.com/XOBUHLKQ/why-do-i-see-a-message-you-appear-to-have-cloned-an-empty-repository-when-using-heroku-git-clone&quot;&gt;found this helpful guide&lt;/a&gt; and worked through it, and eventually I was able to spin up my Strapi app locally.&lt;/p&gt;
&lt;p&gt;I work on Linux, and I had to be careful when installing MongoDB locally. This is because I needed to add MongoDB via the APT package manager, and I inadvertently tried to install a version meant for older Linux OSes. So I broke my computer and had to re-install the OS.&lt;/p&gt;
&lt;p&gt;After that, I decided to stick with the remote service mLab until I can decipher the Docker documentation and get it all running together inside a container.&lt;/p&gt;
&lt;p&gt;Once done, I was able to add content types and fields, and add &lt;a href=&quot;https://cloudinary.com/&quot;&gt;Cloudinary&lt;/a&gt;as an image storage provider, and push my changes to Heroku.&lt;/p&gt;
&lt;h2&gt;Querying the API &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#querying-the-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One last hurdle remained, which was to query the API from GatsbyJS. To start with, I found I kept geting 401 Unauthorised notifications. This is because every content type you define is private by default. You have to enable the public user, or submit auth details, in order to query the data. &lt;a href=&quot;https://strapi.io/documentation/getting-started/quick-start.html#consume-your-api&quot;&gt;Here&apos;s a handy guide on how to do that&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;That was it, I now had a fully functional API hosted for free on Heroku. I could start the app (Heroku&apos;s free tier means that you app will sleep until its needed), run Gatsbyjs, and get my data using the gatsby-source-strapi plugin.&lt;/p&gt;
&lt;h2&gt;Strapi - Ready for Production? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-content-authoring-with-strapijs/#strapi-ready-for-production&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I write this, Strapi is moving steadily towards an initial Beta release. It&apos;s positioning - a self-hosted API dashboard and content authoring platform - is pretty much unique in the JavaScript world. I&apos;m using it to store content for my new personal site &lt;a href=&quot;https://discovermikeoldfield.info/&quot;&gt;https://discovermikeoldfield.info&lt;/a&gt;, and I&apos;m putting it forward at work as a replacement (and enhancement) over those separate, difficult to maintain or retired APIs I mentioned at the start.&lt;/p&gt;
&lt;p&gt;However, this is a product that&apos;s very much in development, and there are certain inconsistencies to watch out for. Plugins and even features may change frequently. I had some serious issues with the date picker in the version I was using, and I couldn&apos;t get the Cloudinary plugin to work so I instead created a text field and uploaded my images to Cloudinary independently.&lt;/p&gt;
&lt;p&gt;However, it still met my requirements and proved to be a product I recommend investigating, and using if it meets your requirements. If you are after this type of thing I ask you to be generous with your support for the time and money the contributors have put into it. It takes not only serious programming chops but also a good investment in design for such a thing to be useable, and I think the Strapi team have achieved that.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Content Authoring with StrapiJs&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Continuous Deployment with Netlify and Hugo</title><link>https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/</guid><description>Over the past few days, I&apos;ve moved tech stack entirely for deliciousreverie.co.uk, from reseller hosting using PHP to cloud hosting on AWS by Netlify, and using continuous deployment. This post details some of the hazards and benefits I encountered. </description><pubDate>Thu, 18 May 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Over the past few days, I&apos;ve moved tech stack entirely for &lt;a href=&quot;http://deliciousreverie.co.uk/&quot;&gt;deliciousreverie.co.uk&lt;/a&gt;, from reseller hosting using PHP to cloud hosting on AWS by Netlify, and using continuous deployment. This post details some of the hazards and benefits I encountered.&lt;/p&gt;
&lt;p&gt;Since I created this blog, I&apos;ve always been a very proud user of Perch CMS and have recently upgraded to Perch 3, which brought loads of enhancements as well as an improved interface. So why the switch?&lt;/p&gt;
&lt;p&gt;The truth of it is that I&apos;ve been paying for reseller hosting with my previous provider, UK Web Solutions Direct, for 7 years. They&apos;ve always been quick to respond to my support questions, and I&apos;d highly recommend them.&lt;/p&gt;
&lt;p&gt;However, since I gave up my freelance work, it came to the point where I only had this website hosted on it, which seemed a bit of a waste. I also wanted to try out Netlify&apos;s continuous deployment service as an alternative to FTP.&lt;/p&gt;
&lt;h2&gt;Building a Hugo Site &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#building-a-hugo-site&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I felt that Hugo was fairly easy to get a grasp of. Once you get around how strange Go templates look initially, I grabbed the theme called base from the Hugo theme repo and began rolling my own.&lt;/p&gt;
&lt;p&gt;I&apos;ve since found that Base theme isn&apos;t maintained and doesn&apos;t contain some of the new shiny that&apos;s present in Hugo 0.20. Just a caution if you were thinking of doing the same. Sadly, there isn&apos;t yet another theme that can replace it to give you a quick leg-up without any CSS already present in the theme.&lt;/p&gt;
&lt;p&gt;The hardest thing I found when using Hugo was ... how to build my site. Hah, yes it&apos;s hilarous, but having used commands like hugo server and hugo new site and hugo new theme it totally passed me by that to actually build my static site, all I needed to do was run hugo.&lt;/p&gt;
&lt;p&gt;There you are. Laugh all you want. I&apos;m maybe being a little bit dense, but I think it&apos;s counterintuitive. Why not hugo build or something?&lt;/p&gt;
&lt;p&gt;So, I had my theme and set up a Netlify repo. Next was deployment.&lt;/p&gt;
&lt;h2&gt;Deploying to Netlify &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#deploying-to-netlify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Netlify&apos;s interface makes deployment pretty painless. Once I authorised GitHub on my account, it slurped the code, installed dependencies, ran the hugo build process ... and failed miserably.&lt;/p&gt;
&lt;p&gt;It failed because currently, by default, Netlify seems to default to an older version of Hugo (v0.16 or something) which meant I was getting build failures.&lt;/p&gt;
&lt;p&gt;I eventually found out that you need to add a netlify.toml file in the root directory to specify the Hugo version you need:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[context.production.environment]
  HUGO_VERSION = &quot;0.20&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That seemed to work! I switched my DNS over to Cloudflare and the site went live!&lt;/p&gt;
&lt;h2&gt;Using Pygments on Netlify &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#using-pygments-on-netlify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One thing I noticed post-launch was a that a Python dependency I had started to use wasn&apos;t working on Netlify. My code blocks would render as plain text, making them unfathomable and messy.&lt;/p&gt;
&lt;p&gt;I came across &lt;a href=&quot;https://discuss.gohugo.io/t/hugo-on-netlify/1505/10&quot;&gt;this post - entitled Hugo on Netlify&lt;/a&gt; - which helped me to see I could add that dependency to Netlify&apos;s build process by adding another config file, requirements.txt to the root, and adding Pygments==2.1.1 as the content.&lt;/p&gt;
&lt;p&gt;So I ran hugo again, added the commit, and waited ... and waited. Quite a long wait this time actually. Pygments does slow Netlify down quite a bit. But the alternative to Pygments is a Javascript library, which would slow down my users. So I don&apos;t mind taking a bit of a hit to save them the extra load time.&lt;/p&gt;
&lt;h2&gt;HTTPS &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#https&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I initially had trouble setting up by DNS with Cloudflare and applying Netlify&apos;s HTTPs certificate. It turns out, after having talked to Netlify, that they don&apos;t have IPv6 yet. Cloudflare adds an AAAA record to your DNS which messes up the HTTPS somehow.&lt;/p&gt;
&lt;p&gt;Once I&apos;d disabled Cloudflare&apos;s CDN, I reapplied the certificate and everything worked fine, and I was able to secure my content.&lt;/p&gt;
&lt;h2&gt;Aims &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#aims&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My site now loads in 1.526 seconds and serves 182kb of content in 8 requests. I&apos;m going to continue to refine this so that hopefully I can find out how to preload fonts and the background image, inline my CSS and JavaScript, or find other improvements that&apos;ll hopefully bring this down as much as I can.&lt;/p&gt;
&lt;p&gt;I also aim to add commenting via &lt;a href=&quot;https://staticman.net/&quot;&gt;Staticman&lt;/a&gt;, contact forms using &lt;a href=&quot;https://formspree.io/&quot;&gt;Formspree&lt;/a&gt;, and &lt;a href=&quot;https://forestry.io/&quot;&gt;Forestry&lt;/a&gt; as a CMS so that I can author posts online.&lt;/p&gt;
&lt;p&gt;Ahh, static sites aren&apos;t so static after all, are they?&lt;/p&gt;
&lt;h2&gt;Verdict &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#verdict&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As the web becomes more bloated, I really believe static site generators are going to be the way to get ahead. Once you add the previously mentioned services, they can adequately compete with some of the lumbering, huge CMSes out there (of which &lt;a href=&quot;https://grabaperch.com/&quot;&gt;Perch&lt;/a&gt; isn&apos;t one, I hasten to add).&lt;/p&gt;
&lt;p&gt;Continuous delivery can a bit cumbersome to manage for some projects in my view. I say this because my build and production environments were different and that caused me a few issues. I&apos;m on the free Netlify plan, which means I get a lot less in terms of build previews. Looking through their documentation, it seems there&apos;s a lot more available on their paid tiers.&lt;/p&gt;
&lt;p&gt;That being said, it&apos;s certainly a lot more safe than the somewhat shakier transfers of FTP/SFTP. One of the benefits became real to me when a friend of mine submitted a pull request, but I was able to see before I merged the code that the change would&apos;ve broken my site, resulting in down time. That was a really useful feature. Now I know I can change my theme, and even if it succeeds locally, if it fails on production you won&apos;t break your site, which I know I&apos;ve done a few times.&lt;/p&gt;
&lt;h3&gt;Addendum &lt;a href=&quot;https://deliciousreverie.co.uk/posts/continuous-deployment-with-netlify-and-hugo/#addendum&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This article was edited to remove the references to custom headers, which aren&apos;t yet available on Netlify yet.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Continuous Deployment with Netlify and Hugo&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Creating my homepage animation</title><link>https://deliciousreverie.co.uk/posts/creating-homepage-animation/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/creating-homepage-animation/</guid><description>I made the original animation for this site when I was using Hugo, but it&apos;s taken some time to refactor to React. Here&apos;s the story of how I achieved that. </description><pubDate>Sat, 21 Nov 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I made the original animation for this site when I was using Hugo, but it&apos;s taken some time to refactor to React. Here&apos;s the story of how I achieved that.&lt;/p&gt;
&lt;p&gt;I wanted this site to be pretty minimal, but a few nice little touches can really make a website stand out. That&apos;s why I made the animation here on the homepage. You have to scroll up &amp;amp; down slowly to see it ...&lt;/p&gt;
&lt;h2&gt;First steps &lt;a href=&quot;https://deliciousreverie.co.uk/posts/creating-homepage-animation/#first-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The original animation was created with both ScrollMagic and GreenSock, two of the most incredibly diverse animation libraries available. The combination of these two is I think the easiest way of creating JavaScript animations when you&apos;re scrolling around, and I have used them to provide some pretty nice effects on sites I&apos;ve worked on before.&lt;/p&gt;
&lt;p&gt;However, the bundle sizes of GreenSock and ScrollMagic are ... quite large. I wanted to rewrite it because (1) I care about how much JavaScript I&apos;m sending down (2) I want to see what I can achieve in plain JavaScript (3) I have the luxury of having no deadlines.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/endymion1818/pen/xrRyXw&quot;&gt;Here&apos;s the original animation on CodePen (137 lines)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Animations, especially ones where you&apos;re telling a story like this one, depend much on timing. I was particularly happy with the specific points that each rabbit became visible in this iteration, especially the last three, where the rabbit appears to be listening out for something, before dashing off the screen. It took a considerable investment of hours to get this right, so I&apos;ve more or less kept this though the other implementations.&lt;/p&gt;
&lt;p&gt;I&apos;ve been told that this animation isn&apos;t prominent enough for most people to notice it. But I think that&apos;s partly the point: I like the idea that most people might not notice there is an animation: after all, most people aren&apos;t looking for that when they come to my site, so I didn&apos;t want to distract further their already distracted minds.&lt;/p&gt;
&lt;p&gt;However, for those that do stop to notice, the brevity almost emphasises the subject matter more: it&apos;s only when we slow down ourselves that we start to notice the wonderful little things that are going on in nature around us.&lt;/p&gt;
&lt;p&gt;The SVG images are free ones I found after trawling through a huge raft of websites selling, giving away, and pretending to give away, svgs.&lt;/p&gt;
&lt;h2&gt;Rebuild in JavaScript &lt;a href=&quot;https://deliciousreverie.co.uk/posts/creating-homepage-animation/#rebuild-in-javascript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this re-implementation I made one significant change: the opacity of each rabbit is controlled by CSS, and doesn&apos;t fade in and out whilst you&apos;re scrolling. It&apos;s a a little bit of a cheat but I quite like how it makes the effect a little more dream like:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/endymion1818/pen/ZEbGXgj&quot;&gt;See it on CodePen&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is only 35 lines, and without any libraries, it&apos;s much faster and better for users.&lt;/p&gt;
&lt;h2&gt;Rebuild to React &lt;a href=&quot;https://deliciousreverie.co.uk/posts/creating-homepage-animation/#rebuild-to-react&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I switched to React I was conscious that my JavaScript bundle had increased quite a lot, and I wanted to try to make compensation for that by implementing some other performance gains before I started getting fancy.&lt;/p&gt;
&lt;p&gt;It took some time to get this to work in React. Building this honestly got me to question whether React is &quot;just javascript&quot;.&lt;/p&gt;
&lt;p&gt;The actual animation code is 43 lines, a significant step up. There were a few tricky things I had to work out too:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://y2in6.csb.app&quot;&gt;View on CodeSandBox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;React re-renders the page at different stages, so needs a way of getting the current element that&apos;s in the DOM. The &lt;code&gt;useRef()&lt;/code&gt; hook allows us to do that. There&apos;s a caveat with that though: the initial value of current is undefined, it wasn&apos;t until I realised I needed to get the elements first then call them inside of a &lt;code&gt;useEffect()&lt;/code&gt; hook.&lt;/p&gt;
&lt;p&gt;It was also easier to use a pre-built hook, react-use-scroll-position, rather than write my own code. It&apos;s pretty minimal so I don&apos;t mind that too much.&lt;/p&gt;
&lt;h2&gt;TypeScript implementation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/creating-homepage-animation/#typescript-implementation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;m using this one on my homepage now, but with TypeScript:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Rabbits = () =&amp;gt; {

  if(typeof window === &apos;undefined&apos;) { return &amp;lt;&amp;gt;&amp;lt;/&amp;gt; }
  if(window.innerWidth &amp;lt; 998 ) { return &amp;lt;&amp;gt;&amp;lt;/&amp;gt; }

  const scrollYPosition = useScrollYPosition()

function getScrollPercent() {
    return (
        scrollYPosition || document.body.scrollTop) / (
          (document.documentElement.scrollHeight || document.body.scrollHeight) - document.documentElement.clientHeight
      ) * 100;
}

  const useScrollHeightToChangeOpacity = (
    domElement:HTMLDivElement,
    inHeight:number,
    outHeight:number
  ) =&amp;gt; {
    if (getScrollPercent() &amp;gt; inHeight &amp;amp;&amp;amp;  getScrollPercent() &amp;lt; outHeight){
      domElement.style.opacity = &apos;0.8&apos;
    } else {
      domElement.style.opacity = &apos;0&apos;
    }
  }

  const treeSummer = useRef(null)
  const rabbitOne = useRef(null)
  const rabbitTwo = useRef(null)
  const rabbitThree = useRef(null)
  const rabbitFour = useRef(null)
  const rabbitFive = useRef(null)
  const rabbitSix = useRef(null)
  const rabbitSeven = useRef(null);

    useEffect(() =&amp;gt; {
      useScrollHeightToChangeOpacity(rabbitOne.current!, 5, 10)
      useScrollHeightToChangeOpacity(rabbitOne.current!, 8, 15)
      useScrollHeightToChangeOpacity(rabbitTwo.current!, 20, 25)
      useScrollHeightToChangeOpacity(rabbitThree.current!, 30, 40)
      useScrollHeightToChangeOpacity(rabbitFour.current!, 45, 60)
      useScrollHeightToChangeOpacity(rabbitFive.current!, 65, 80)
      useScrollHeightToChangeOpacity(rabbitSix.current!, 85, 90)
      useScrollHeightToChangeOpacity(rabbitSeven.current!, 90, 95)
    }, [getScrollPercent]);

  return (
      ...
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The hardest part of the TypeScript implementation was to find out how to get the .current value of useRef(), because the initial value as you can see is null (the dom element doesn&apos;t exist yet). Unfortunately the only help you get from the compiler is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Argument of type &apos;null&apos; is not assignable to parameter of type &apos;HTMLDivElement&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To tell the compiler that it&apos;s not possible for this value to be null at this point, you append it with a !, the non-null assertion operator.&lt;/p&gt;
&lt;p&gt;Annoying, but not annoying enough for me to change my position on TypeScript.&lt;/p&gt;
&lt;h2&gt;Future plans &lt;a href=&quot;https://deliciousreverie.co.uk/posts/creating-homepage-animation/#future-plans&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have to ask why I chose this particular scene, it&apos;s because it reminds me both of my childhood days roaming the countryside of County Durham when such wildlife was abundant, and the film &quot;Watership Down&quot; (I watched that film when I was far too young!).&lt;/p&gt;
&lt;p&gt;Originally, I planned ot have different animations on other pages too, like a bird or butterfly crossing the viewport, and a little girl sitting on a tree swing. I&apos;m still thinking about whether or not to implement those, since the SVGs are quite large and it might not be good for my bundle size.&lt;/p&gt;
&lt;p&gt;However I do really like how much character it lends to the site.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Creating my homepage animation&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>My Recipe for Crispy Beef</title><link>https://deliciousreverie.co.uk/posts/crispy-beef-recipe/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/crispy-beef-recipe/</guid><description>I&apos;ve tried several recipes for crispy beef, and have finally gained confidence enough to make it on my own terms. Here&apos;s a simplified version that I now follow. It&apos;s quite rough, and it&apos;s meant to be adapted. </description><pubDate>Thu, 28 Jul 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve tried several recipes for crispy beef, and have finally gained confidence enough to make it on my own terms. Here&apos;s a simplified version that I now follow. It&apos;s quite rough, and it&apos;s meant to be adapted.&lt;/p&gt;
&lt;p&gt;The trick with crispy beef is to keep it dry enough so that it will firm up on cooking. This also depends on getting enough heat for it to caramelise.&lt;/p&gt;
&lt;p&gt;Ingredients:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Minced beef&lt;/li&gt;
&lt;li&gt;Keçap manis (or a syrupy mix of dark soy sauce and brown sugar)&lt;/li&gt;
&lt;li&gt;Sesame oil&lt;/li&gt;
&lt;li&gt;Corn flour&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This isn&apos;t a comprehensive list. You can add other things as you go like red peppers, spring onions, sesame seeds, chilli flakes.&lt;/p&gt;
&lt;h2&gt;Step 1 Mix beef &lt;a href=&quot;https://deliciousreverie.co.uk/posts/crispy-beef-recipe/#step-1-mix-beef&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mix the beef in some corn flour with your hands until its dry and flaky. Add a good squeeze of keçap manis (about 200ml). Keçap manis is basically just dark soy sauce and sugar, so don&apos;t feel compelled to buy this item if you want to mix your own.&lt;/p&gt;
&lt;p&gt;By the end of this mix, you should have a disgusting moist mixture. Don&apos;t worry about the consistency at this stage, things will improve with the application of some heat!&lt;/p&gt;
&lt;h2&gt;Step 2 Cook &lt;a href=&quot;https://deliciousreverie.co.uk/posts/crispy-beef-recipe/#step-2-cook&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Its important to heat the sesame oil before putting in the beef. This is so the oil doesn&apos;t soak into the meat but instead heats up the sugar until it caramelises. Put in the whole mixture and keep stirring and mixing until it caramelises.&lt;/p&gt;
&lt;p&gt;Gradually the mixture will darken and separate. Take it off the heat before it starts burning but when it&apos;s sufficiently dark and crispy.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:My Recipe for Crispy Beef&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Deliverables and Trust</title><link>https://deliciousreverie.co.uk/posts/deliverables-and-trust/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/deliverables-and-trust/</guid><description>It&apos;s vitally important to have success before you start a project-perhaps the most critical stage to have success. But how far should we go in order to get our clients on board? Should we try to wow clients into signoff? Here&apos;s why that is a bad idea.</description><pubDate>Fri, 27 Feb 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;It&apos;s vitally important to have success before you start a project-perhaps the most critical stage to have success. But how far should we go in order to get our clients on board? Should we try to &quot;wow&quot; clients into signoff? Here&apos;s why that is a bad idea.&lt;/p&gt;
&lt;p&gt;So you&apos;re taking this proposal to your new client. You sit down in the boardroom, a little nervous but trying to hide it. You naturally have done everything in your power to ensure the success of the meeting. What will win over this set of decision makers and stakeholders to invest in your proposal?&lt;/p&gt;
&lt;p&gt;I can see why we are so tempted to take in visual designs as deliverables to these early meetings. It&apos;s only natural to want to impress ... but there&apos;s a danger lurking at this tender stage of the project which could ruin the conclusion, and cause stress and frustration for every team member in between.&lt;/p&gt;
&lt;p&gt;Swaying the opinions of stakeholders is vitally important. But I argue there are 2 ways of doing it. We can win stakeholders over with demonstration of genuine professionalism, interest in their affairs, and understanding of their business needs.&lt;/p&gt;
&lt;p&gt;It doesn&apos;t take much for a client to feel let down. Unmet expectations are a slippery slope.&lt;/p&gt;
&lt;p&gt;So take time to get to know their assumptions. Ask these kinds of questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why do you want a site redesign?&lt;/li&gt;
&lt;li&gt;What are your business goals for this project?&lt;/li&gt;
&lt;li&gt;At the end of the project, what would you consider to be successful?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At this stage it&apos;s not just the business goals that are important. Depending on the client, it&apos;s beneficial to try to train them to see things from their users perspective. Help them to see that their new site is for their users—new and repeat customers. If they are aware of that, if you&apos;re an advocate for the user in these meetings, you demonstrate professionalism and help them see that you have business goals in mind.&lt;/p&gt;
&lt;p&gt;Deliverables and trust are difficult to negotiate on. But if we avoid &quot;wowing&quot; our clients and instead seek to understand them, the outcome can be much better—for us and for them.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Deliverables and Trust&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Delaying click event in vanilla JavaScript</title><link>https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/</guid><description>I was inspired by Rachel Nabors&apos; site to add a short delay to users&apos; clicking on a link on my website, so that I could add a brief animation that would run before the redirection took place. Here&apos;s how I did it. </description><pubDate>Tue, 22 Mar 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I was inspired by Rachel Nabors&apos; site to add a short delay to users&apos; clicking on a link on my website, so that I could add a brief animation that would run before the redirection took place. Here&apos;s how I did it.&lt;/p&gt;
&lt;p&gt;&quot;Animation&quot; seems to be the theme of my year. Animation is what I think is going to be one of the big differentiator on websites in the coming months and years. And I really, really love the CSS animations API.&lt;/p&gt;
&lt;p&gt;I&apos;ve used waypoints.js a lot lately so that I can run animations at different stages during the time that a user scrolls around my site. But for my personal blog, I wanted to do something a little trickier.&lt;/p&gt;
&lt;h2&gt;My Requirements&lt;/h2&gt;
&lt;p&gt;I wanted to fade in page elements when the user arrives, and fade them out again when they left. I know that it could potentially have been easier to write this in jQuery using the delay() function, but I decided to go the route of a custom bit of JavaScript. It was a good practice exercise for me, and I don&apos;t want to load the jQuery library here unless I have to.&lt;/p&gt;
&lt;h3&gt;The Code &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#the-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It took me a while to come up with the strategy for this. I started looking at onpageunload(), hoping to add a delay to that. But that&apos;s quite locked down, and didn&apos;t work for my requirements.&lt;/p&gt;
&lt;p&gt;After I had fiddled for a while, I ran up against a brick wall. For some reason, my JS event listener wasn&apos;t working properly.&lt;/p&gt;
&lt;p&gt;So I turned to StackOverflow and posed the question, which helped immensely, and validated what I was already trying to do. &lt;a href=&quot;https://stackoverflow.com/questions/36125391/vanilla-js-delay-click-event-to-add-animation/36126631&quot;&gt;The thread is here if you&apos;d like to read it&lt;/a&gt;.&lt;/p&gt;
&lt;h4&gt;Step 1 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#step-1&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Reading some other posts on Stack Overflow helped me see that I needed to grab all of the &amp;lt;a&amp;gt; tags into a variable, then listen for an event.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var links = document.getElementsByTagName(&apos;a&apos;);for( var i=0,il = links.length; i&amp;lt; il; i ++ ){links[i].onclick = clickHandler;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Step 2 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#step-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Then, I needed a function called clickHandler that would fire on click:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function clickHandler(event) {

event.preventDefault();

var travelTo = this.getAttribute(&quot;href&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first job of this handler was to stop the link from being followed, using preventDefault(), then to grab the link the user was going to so that we could use it later on.&lt;/p&gt;
&lt;h4&gt;Step 3 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#step-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The next step was to add my animation class to the elements I wanted:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var animOut = document.getElementsByClassName(&quot;animateOut&quot;);
// iterate `animOut` 
elementsfor (var i = 0; i &amp;lt; animOut.length; i++) {   
  // add `out` `className` to `animOut` element at index `i`   
  animOut[i].classList.add(&quot;out&quot;);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks for each element that has a class of animOut, and adds a further class, out when the function runs.&lt;/p&gt;
&lt;h4&gt;Step 4 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#step-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Next, we needed to send the user on their way using the travelTovariable we stored earlier and using the setTimeout function to add a bit of a delay:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setTimeout(function() {  window.location.href = travelTo;}, 1000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I played with the durations quite a bit. It was important for me not to make the animation and/or the delay too long because I thought it would probably be distracting to the user. I reduced it from 1 second to just half a second for this very reason.&lt;/p&gt;
&lt;h4&gt;The Result &lt;a href=&quot;https://deliciousreverie.co.uk/posts/delaying-click-event-vanilla-javascript/#the-result&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Well, click on a link and see it working!!&lt;/p&gt;
&lt;p&gt;It was great to be able to work this out, and the Stack Overflow community once again proved to be a fantastic resource.&lt;/p&gt;
&lt;p&gt;I must also credit Rachel Nabors for inspiring me, since I first saw this being used &lt;a href=&quot;https://rachelnabors.com/&quot;&gt;on her website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s the full code in a Gist if you&apos;d like to adapt it for your site:&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Delaying click event in vanilla JavaScript&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>On Having Delusions of Grandeur</title><link>https://deliciousreverie.co.uk/posts/delusions-of-grandeur/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/delusions-of-grandeur/</guid><description>I&apos;m not as good at this as I thought I was. This much became apparent to me recently, when I tried to run with some industry leaders and found that my abilities in web development are still comparatively lacking. </description><pubDate>Tue, 02 Jun 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m not as good at this as I thought I was. This much became apparent to me recently, when I tried to run with some industry leaders and found that my abilities in web development are still comparatively lacking.&lt;/p&gt;
&lt;p&gt;As developers, we find no comfort in the thought that we&apos;ll never know enough about the languages we use, the frameworks we work in, the libraries we utilise and the platforms that underpin our whole industry. It&apos;s impossible to absorb and retain all of this information.&lt;/p&gt;
&lt;p&gt;What really shook me up was discovering the &lt;a href=&quot;https://validator.w3.org/&quot;&gt;W3C Validator tool&lt;/a&gt;, and finding out what I&apos;ve being doing wrong all this time. I&apos;ve started to retroactively go through my sites and fix the errors that were revealed, but it still smarts that I&apos;ve been ignoring these standards for far too long.&lt;/p&gt;
&lt;p&gt;It doesn&apos;t help either that I wasn&apos;t even aware of them.&lt;/p&gt;
&lt;p&gt;What I take away from this experience is that I need to &quot;stay foolish&quot;, and instead of having delusions of grandeur, be prepared to take knocks like this as jolts of advancement. I realise too that I want to improve—I&apos;m not content to go through life without continuously improving my professional abilities.&lt;/p&gt;
&lt;p&gt;And that, I think, can count for something.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:On Having Delusions of Grandeur&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Deploying Nx monorepos to Netlify in 2023</title><link>https://deliciousreverie.co.uk/posts/deploying-monorepos-to-netlify-in-2023/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/deploying-monorepos-to-netlify-in-2023/</guid><description>Monorepos with Nx on Netlify has been my stack of choice over the past few years. I really enjoy using both tools to publish an ever-growing series of websites for my hobbies. But there have been a few changes to both Nx and Netlify in that time. This article updates one on the Netlify blog that&apos;s a little out of date.</description><pubDate>Wed, 31 May 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Monorepos with Nx on Netlify has been my stack of choice over the past few years. I really enjoy using both tools to publish an ever-growing series of websites for my hobbies. But there have been a few changes to both Nx and Netlify recently. This article updates one on the Netlify blog that&apos;s a little out of date.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://levelup-styleguide.netlify.app/blog/2020/04/21/deploying-nx-monorepos-to-netlify/&quot;&gt;Here&apos;s the original article,&lt;/a&gt; which is still really useful and goes into a lot of background as to why you might want to use a monorepo to build your frontends. It&apos;s a great article and I recommend you read the first half just to get some background. But later in the article the author for some reason digresses into setting up a CircleCI pipeline. Personally I don&apos;t find a lot of value in doing that for my frontends, and I think it distracts from the core subject a little.&lt;/p&gt;
&lt;h2&gt;Nx Cache&lt;/h2&gt;
&lt;p&gt;The first thing I wanted to mention that has changed is Nx aggressive caching. Their &lt;a href=&quot;https://nx.dev/nx-cloud/intro/what-is-nx-cloud&quot;&gt;Nx Cloud&lt;/a&gt; plugin automatically pushes builds to their cloud without any configuration, making it accessible to your pipeline in Netlify. &lt;a href=&quot;https://answers.netlify.com/t/support-guide-nx-monorepo-site-does-not-reflect-changes-after-build/73657&quot;&gt;As this Netlify support article shows,&lt;/a&gt; you might see in your build logs &quot;0 new files to upload&quot;. This means your build has been diffed with what&apos;s stored on Nx Cloud and there were no changes.To get around this you can append --skip-nx-cache to your build command.&lt;/p&gt;
&lt;p&gt;So do we need a build plugin at all? In fact, we don&apos;t unless you hit some edge case where you cannot use Nx Cloud. This is probably the only reason you might want to write your own implementation.&lt;/p&gt;
&lt;p&gt;To utilise Nx Cache, run your deploy command as usual. It will discover if there have been code changes and deploy based on those changes. Otherwise it will skip the build and deployment steps. If you still want to deploy (perhaps you have changes in an external CMS, something that won&apos;t be detected on build), then add `--skip-nx-cache` to the build command and re-run it. You should then see it has fetched fresh data from your CMS and built the site anew.&lt;/p&gt;
&lt;p&gt;In the case where you cannot use Nx Cloud, here&apos;s how to do it:&lt;/p&gt;
&lt;h2&gt;Updated Build Plugin&lt;/h2&gt;
&lt;p&gt;So the original article wasn&apos;t super clear about where your build plugin should be located. I put it in the tools directory under a new directory called skip-build. You should then have a netlify.toml in the root of your project referencing the folder (not the JS file) in the project:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[[plugins]]
package = &quot;./tools/skip-build&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now there are a few updates to the plugin too, let&apos;s go through them.&lt;/p&gt;
&lt;p&gt;First, as this utility function returns a boolean you&apos;ll notice I renamed it to something that&apos;s much easier to follow. This new name shows the intent of the function so it makes it much easier for a newcomer to identify what&apos;s going on.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function hasProjectChanged(currentProject, lastDeployedCommit, latestCommit) {
  const execSync = require(&apos;child_process&apos;).execSync;
  const getAffected = `npm run nx print-affected --base=${lastDeployedCommit} --head=${latestCommit}`;
  const output = execSync(getAffected).toString();
  //get the list of changed projects from the output
  const sanitizedOutput = output
  .replace(/\r?\n|\r/g, &apos;&apos;)
  .replace(&apos;&amp;gt; frontends@0.0.0 nx&amp;gt; nx print-affected&apos;, &apos;&apos;)
  const changedProjects = JSON.parse(sanitizedOutput).projects;
  
  if (changedProjects.find(project =&amp;gt; project === currentProject)) {
    return true;
  } else {
    return false;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The other thing I did here was to sanitise the output of the execSync function, since I found there were a lot of unescaped characters in the output, and it also contained some other metadata. Once I replaced these with empty space, I was able to parse the valid JSON and be able to find out if my current project has changed or not.&lt;/p&gt;
&lt;p&gt;Netlify&apos;s API has also changed slightly, instead of having onInit, we now have onPreBuild, so I&apos;ve updated that here:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  onPreBuild: ({ utils }) =&amp;gt; {
    const currentProject = process.env.PROJECT_NAME;
    const lastDeployedCommit = process.env.CACHED_COMMIT_REF;
    const latestCommit = &apos;HEAD&apos;;
    const projectHasChanged = hasProjectChanged(
      currentProject,
      lastDeployedCommit,
      latestCommit
    );
    if (!projectHasChanged) {
      utils.build.cancelBuild(
        `Build was cancelled because ${currentProject} was not affected by the latest changes`
      );
    }
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I had this plugin I was able to see that builds were skipped when I didn&apos;t update the project.&lt;/p&gt;
&lt;p&gt;The only trouble I&apos;m now having is that all of my content is stored in Webiny CMS, and the build plugin is unaware of whether that content has changed. I&apos;m thinking of doing an API call out from the build to discover if there are any recently published articles ...&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Deploying Nx monorepos to Netlify in 2023&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Deserializing Data in GatsbyJS</title><link>https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/</guid><description>One of the great strengths in static site generator Gatsbyjs is the node API but it can present a few issues in certain circumstances when content is stored as escaped HTML, such as in WordPress posts and pages. Here&apos;s how we recently dealt with this issue when using react-helmet.</description><pubDate>Sat, 17 Mar 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;One of the great strengths in static site generator Gatsbyjs is it&apos;s node API. This API gives Gatsby flexibility by allowing data to be transformed from myriad sources into a format that can easily be rendered as HTML: JSON. However, this can present a few issues when content is stored as escaped HTML, such as in WordPress posts and pages. Here&apos;s how a colleague and I worked around a tricky problem we discovered when working with content pulled in from WordPress.&lt;/p&gt;
&lt;p&gt;I recently built a GatsbyJS site that stored content in Markdown and used NetlifyCMS to allow content authors to work directly with the site in editing and adding content. Using Markdown with NetlifyCMS was an interim solution. We needed to build a site in only a few days, but knew that requirements would at some point grow substantially, at which point we wanted to switch to a WordPress backend.&lt;/p&gt;
&lt;p&gt;When I switched to WordPress it took me only a few hours to implement 95% of the work, most of which was spent generating the existing content as WordPress pages. I modified my gatsby-node.js file and my page template, but I came unstuck when using Helmet to generate page titles.&lt;/p&gt;
&lt;h2&gt;The Issue &lt;a href=&quot;https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/#the-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using the existing data structure I was able to generate a page layout that looked like this:&lt;/p&gt;
&lt;p&gt;This results in the result we expected, the content rendered as HTML inside the respective tags.&lt;/p&gt;
&lt;p&gt;However, the post.title is stored as escaped HTML, so when you try to pop the post.title into the meta tag using Helmet, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Helmet title={`${post.title}`} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;you get whatever markup is stored. This is fine in most cases ... however, in WordPress land you&apos;ll quite often find people using things like &amp;lt;br&amp;gt; tags to adapt their layouts a little, or in my case, using a dash. When rendered as a meta tag, that looked like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mytitle \&amp;amp;#8221; contains a dash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not great for presentation, or SEO.&lt;/p&gt;
&lt;h2&gt;The Workaround. &lt;a href=&quot;https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/#the-workaround.&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There wasn&apos;t a simple way around this issue that my colleague &lt;a href=&quot;https://twitter.com/Mosh1e&quot;&gt;David Hewitt&lt;/a&gt; or I could find.&lt;/p&gt;
&lt;p&gt;We knew we needed to parse the element so that it returned as HTML again, but there wasn&apos;t an easy way to do this in Gatsbyjs. We knew we could use the DOM parser to achieve these results but here there was no document, hence no DOM.&lt;/p&gt;
&lt;p&gt;In the end we turned to the xmldom package on NPM, and pulled the DOMParser method in, decoding the string and then grabbing the textcontent.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;
import Helmet from &apos;react-helmet&apos;
import { DOMParser } from &apos;xmldom&apos;

class pageTemplate extends React.Component {
  render() {

    const post = this.props.data.wordpressPage;
    const siteTitle = this.props.data.site.siteMetadata.title;

    const dom = new DOMParser().parseFromString(`&amp;lt;div&amp;gt;${this.props.data.wordpressPage.title}&amp;lt;/div&amp;gt;`);
    const decodedString = dom.childNodes[0].textContent;

      return (
      &amp;lt;div&amp;gt;
        &amp;lt;Helmet title={`${decodedString} | ${siteTitle}`} /&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Showing Gatsby&apos;s Strengths &lt;a href=&quot;https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/#showing-gatsby&apos;s-strengths&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although we encountered an issue here, in a lot of ways it has hilighted to me the strengths of GatsbyJS, not what you might perceive as weaknesses.&lt;/p&gt;
&lt;p&gt;In the first instance, the issue originated from WordPress which stores escaped HTML instead of JSON, MarkDown or any other format. I can see the benefits to this (being able to add &amp;lt;br&amp;gt; tags to titles etc) but it does present some unique challenges—and not just to GatsbyJS.&lt;/p&gt;
&lt;p&gt;This has also hilighted one of the fantastic strengths of GatsbyJS:- that I could pull in a package from NPM and use it in my build process. This opens up a world of possibility and makes GatsbyJS a lot more customizeable than I had thought previously.&lt;/p&gt;
&lt;p&gt;And the other strength is that it still took me only a few hours to switch from using MarkDown files to a WordPress backend, such is the strength of the node API and the gatsby-source-wordpress in the way that it transforms data so that there was no difference between the two builds.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/4543&quot;&gt;I&apos;ve filed an issue on GatsbyJS on the repo&lt;/a&gt;, but there are a few issues around how to write a normalizer to deal with this. For instance, in most cases we do want to use the escaped HTML, so that the Title field is rendered in the component. It&apos;s only when using Helmet that we want the string deserialized.&lt;/p&gt;
&lt;p&gt;So I&apos;m not sure how to proceed with this .. at least there&apos;s a workaround for now, and I&apos;m sure as a community we can address this issue so that we can drop the external dependency or build it into the existing processes in some way.&lt;/p&gt;
&lt;h2&gt;UPDATE: use CreateNodeField API &lt;a href=&quot;https://deliciousreverie.co.uk/posts/deserializing-data-in-gatsbyjs/#update:-use-createnodefield-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So the way I rendered the title using the xmldom library was the best I could do at the time ... however, since I&apos;ve learned more about Gatsby&apos;s API, I can now understand why transforming the title on the frontend isn&apos;t the best idea.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;it&apos;s not performant. Users have to download that library before they see a rendered title. This has an impact on how fast people can see the rendered site ... Gatsby is all about performance, so this is a bad idea&lt;/li&gt;
&lt;li&gt;rendering on the frontend can cause issues. Sometimes rehydration doesn&apos;t work properly when you&apos;re doing heavy manipulation on the frontend ... that&apos;s why we have backends, after all.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&apos;s a better way to do it:&lt;/p&gt;
&lt;p&gt;In your gatsby-node.js file&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { decode } from &apos;he&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the onCreateNode function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;createNodeField({
  node,
  name: &apos;renderedTitle&apos;,
  value: decode(node.title.rendered)
})
```&quot;&lt;/code&gt;&lt;/pre&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Deserializing Data in GatsbyJS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Design is Broken</title><link>https://deliciousreverie.co.uk/posts/design-is-broken/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/design-is-broken/</guid><description>I was getting nowhere. For the previous four years, I had been self employed and had managed the transition from print design to web design. I was enjoying life, but then a desire to build on my success as part of a forward thinking and dynamic team motivated me to submit my CV to a number of local agencies.</description><pubDate>Sat, 14 Feb 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I was getting nowhere. For the previous four years, I had been self employed and had managed the transition from print design to web design. I was enjoying life, but then a desire to build on my success as part of a forward thinking and dynamic team motivated me to submit my CV to a number of local agencies.&lt;/p&gt;
&lt;p&gt;But I was getting nowhere.&lt;/p&gt;
&lt;p&gt;I had had a few interviews, and turned down one or two opportunities on principle. I knew the market was competitive, but the skill set I was presenting - that of a web designer - wasn&apos;t generating enough traction. Or at least, not the right kind of traction.&lt;/p&gt;
&lt;p&gt;Then I hit on an idea. I would change one word on my profile in order to highlight web development instead of straight design. Yes, it&apos;s true that I loved design. I could argue about typefaces until the cows come home, I&apos;d fuss for hours about alignment, think twice or three times about how colours I used might create the desired resonance for a piece of artwork or a page on a website. But I also loved wrangling with Sass, delving into PHP and had begun to tackle JavaScript seriously. I admit I had a way to go to perfecting my skills but I was ready for a challenge, and was honest about this in my approach to prospective employers.&lt;/p&gt;
&lt;p&gt;So I switched the title from Web Designer to Web Developer - and then things started to change.&lt;/p&gt;
&lt;h3&gt;What is a designer? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/design-is-broken/#what-is-a-designer&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&quot;A design is an ecosystem. Disrupt one thing and you disrupt others. A designers work is to foresee what will be disrupted and how.&quot; - Robert Hoekman Jr&lt;/p&gt;
&lt;p&gt;What struck me when I began to read The Tao of User Experience is that design is no longer design.&lt;/p&gt;
&lt;p&gt;Design is still about wrangling with the placement of elements on a page - so that content could fit within the desired space and convey the desired message. But it has become so much more than that.&lt;/p&gt;
&lt;p&gt;The far larger part of design used to comprise of nuances of spacing, of detailed discussions about Gill Sans, it&apos;s history and pedigree. About spot colours and page bleed. All these are vital factors in print but these elements do not factor in web design.&lt;/p&gt;
&lt;h3&gt;Where it differs &lt;a href=&quot;https://deliciousreverie.co.uk/posts/design-is-broken/#where-it-differs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rather than a reader, you have a user. Instead of a page, you have a system (known as a web site) and designers can no longer afford the somewhat arrogant approach of being unchallengeable masters or stroppy perfectionists because they are &apos;creatives&apos;. This approach simply does not work in modern web design.&lt;/p&gt;
&lt;p&gt;Designing a website is not mere decoration. To design a site one must properly examine what the end users will do, coupled with the business objectives of the client, and going on evidence to back up approaches not just to appearance but to interactive elements as well.&lt;/p&gt;
&lt;p&gt;Especially to interactive elements.&lt;/p&gt;
&lt;p&gt;A user doesn&apos;t interact with a page by reading it. He may scarcely read anything at all. But he will interact with it, and this is becoming the vital factor in web design. What happens when that button is pressed? How long does the page take to load? At what stage in the users journey are they likely to encounter this page? What happens when interactivity is driven by touch rather than a mouse?&lt;/p&gt;
&lt;p&gt;These factors mean that the definition of design has changed. We are no longer designing static documents, we are designing interactions. Designers are also architects. We must be. Otherwise we must be merely decorators.&lt;/p&gt;
&lt;p&gt;And I&apos;m not in favour of allowing a decorator to draw up plans for my house.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Design is Broken&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A Developer&apos;s Guide to Professional Development</title><link>https://deliciousreverie.co.uk/posts/developers-guide-professional-development/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/developers-guide-professional-development/</guid><description>How do we not get overloaded with what&apos;s going on in web development, but not left behind either? Should we try to keep up with latest trends that might fade away into nothing? Here&apos;s my take on how (and why) I continue to learn about web development. </description><pubDate>Sun, 19 May 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I got asked today why I was applying to attend two different conferences this year. Neither of these conferences were cheap; I feel very lucky to have a training budget where I work. On top of these my company generously provides me with access to 2 learning platforms as to help me become a better developer. As well as that, I pay for another learning platform myself. Is so much needed to keep ahead?&lt;/p&gt;
&lt;p&gt;Is any of it really necessary?&lt;/p&gt;
&lt;h1&gt;Implementation Details &lt;a href=&quot;https://deliciousreverie.co.uk/posts/developers-guide-professional-development/#implementation-details&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The one thing constant about web development is change. Our industry iterates very quickly, and things that were best practice or popular a year ago soon make way for newer ways of doing things.&lt;/p&gt;
&lt;p&gt;One good example of this is state management in React. Only a short time ago, Redux was a standard requirement on front end job ads, it was almost inseperable from React itself.&lt;/p&gt;
&lt;p&gt;Now there are other ways of having a data layer on the frontend that can be considered alternatives to Redux, like React&apos;s own Context API, or Apollo GraphQL.&lt;/p&gt;
&lt;p&gt;To keep my skillset relevant, and make sure I can easily switch jobs if I should need to, I feel I need to keep up with these trends.&lt;/p&gt;
&lt;p&gt;I think of this part of my learning as the implementation detail of what we do. We need to keep relatively informed about new ways of doing things (even if we don&apos;t know them in depth) so that we can adapt if we need to.&lt;/p&gt;
&lt;p&gt;To continue to develop in this regard, I mostly follow tutorials on one of the learning platforms I mentioned. But I also think it&apos;s important to get a more general overview by attending a conference that might cover several of these ideas in one go.&lt;/p&gt;
&lt;p&gt;But there&apos;s something else that I think is important too.&lt;/p&gt;
&lt;h1&gt;Wider Implications &lt;a href=&quot;https://deliciousreverie.co.uk/posts/developers-guide-professional-development/#wider-implications&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I think it&apos;s also very valuable to keep a close eye on the wider implicationsof web development as an industry, and its intersection with others, what&apos;s going on in the wider world, and how that might affect what we do as developers.&lt;/p&gt;
&lt;p&gt;There might be new technologies or ideas brimming on the horizon which could explode into our day-to-day development work. If we don&apos;t watch out for that we could be caught unaware.&lt;/p&gt;
&lt;p&gt;An example of this could be responsive design. When &quot;A Dao of Web Design&quot; by John Allsop was first published on &quot;A List Apart&quot; it was an early indicator that something was on the horizon.&lt;/p&gt;
&lt;p&gt;Later, when Ethan Marcotte published &quot;Responsive Web Design&quot; which made practical application of Allsop&apos;s ideas, we could begin to see how our industry would be altered forever.&lt;/p&gt;
&lt;p&gt;To cover this aspect of my development I try to keep up with people I know who are thinking along these lines. I also subscribe to A List Apart and Smashing Magazine, two publications who are at the bleeding edge of web development.&lt;/p&gt;
&lt;p&gt;But also, conferences such as Mozilla&apos;s MozFest, or JAMStack Conf, tackle wider issues such as privilege and equality, the latest ideas from the CSS Working Group, and new approaches to systems architecture.&lt;/p&gt;
&lt;h1&gt;Continuing... &lt;a href=&quot;https://deliciousreverie.co.uk/posts/developers-guide-professional-development/#continuing...&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I think as developers we should be aware of our &quot;CPD&quot; (continuing Professional Development), much like an architecht or doctor would keep up with their industries, and particularly with that&apos;s happening in their area of expertise.&lt;/p&gt;
&lt;p&gt;The Web Development industry must be one of the only self-taught professional industries out there that demands so much of it&apos;s practitioners. We can&apos;t possibly keep up with it all. I certainly can&apos;t.&lt;/p&gt;
&lt;p&gt;I think it&apos;s enough to keep and overview of the trends and latest news just so we aren&apos;t taken completely by surprise when something changes, and choose to deep dive into subjects that appeal to us, or are relevant to our situations.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A Developer&apos;s Guide to Professional Development&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Don&apos;t fear the JAMStack</title><link>https://deliciousreverie.co.uk/posts/dont-fear-jamstack/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/dont-fear-jamstack/</guid><description>WordPress has long been the de facto publishing platform for the web. But many developers are hearing more about the JAMStack. Here&apos;s why I switched, and my take on where I think things are going to go from here. </description><pubDate>Thu, 03 Sep 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;WordPress has long been the de facto publishing platform for the web. But many developers are hearing more about the JAMStack. Here&apos;s why I switched, and my take on where I think things are going to go from here.&lt;/p&gt;
&lt;p&gt;I would like to acknowledge and address some recent efforts of those in WordPress circles to be vocal against the new approaches to publishing that have arisen in recent years, most significantly these 2 that I&apos;ve seen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;WordPress Co-Founder Matt Mullenweg Is Not a Fan of JAMstack&quot;&lt;/li&gt;
&lt;li&gt;Tweet: &quot;Switching back to WordPress after a year on Hugo&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;First of all, I must acknowledge a debt of gratitude to WordPress. The efforts the team have made to democratise publishing over the past 20 years have been hugely successful, and I still am really happy that there&apos;s a solution out there that people with little technical knowledge can utilise. No other publishing platform has done that, and even now its real competitors cannot achieve this because they are closed silos.&lt;/p&gt;
&lt;p&gt;However, a democracy can never consist of one. A lot has happened in recent years which has preceded a sea change for a lot of developers to the JAMStack, a myriad of alternative separate technologies – one that still includes WordPress, but which is radically different from the approach it has taken.&lt;/p&gt;
&lt;h2&gt;Progression &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dont-fear-jamstack/#progression&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For me, this change happened when I realised I was struggling to progress as a developer, or even to find enjoyable work, where I could produce things I could be proud of. And that in order to move forward, I needed to move away from that platform. I found 3 things that were damaging my development career:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The WordPress API is often inconsistent. get_the_content() vs the_content(), WP_Post vs WP_Term, differing returns (sometimes void, sometimes not) for different get_*() functions ... trying to find my way around this API could never help someone cultivate good development habits, or understand how an API should work.&lt;/li&gt;
&lt;li&gt;WordPress hasn&apos;t moved on from PHP 5. This means learning newer aspects of the language has been impossible and left me unprepared to work on other platforms. I could only get another job working with WordPress, which meant I was unlikely to ever move out of the cottage industry that provides.&lt;/li&gt;
&lt;li&gt;WordPress development practices haven&apos;t moved with the times. I worked for a short time on a Laravel project, and I was dumbfounded by things I had to learn, like build pipelines, database migrations, object oriented language principles, and even how to use a package manager.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In short, I was faced with a decision: either continue to develop solely with WordPress for the rest of my career, or make the jump. I had already seen a large number of developers grow dissatisfied because of these issues, and similarly move off the platform.&lt;/p&gt;
&lt;p&gt;I decided to do the same just around the time that Gutenberg was being released.&lt;/p&gt;
&lt;h2&gt;Gutenberg &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dont-fear-jamstack/#gutenberg&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whilst the new editor interface, Gutenberg, was a big step forward for users of the platform, it&apos;s no secret that it alienated a lot of developers.&lt;/p&gt;
&lt;p&gt;Largely this was because of the ease with which page layouts could be created without developer input, bypassing the finesse and structure that a developers&apos; input brings to a website. Now it&apos;s even easier for individuals to create blogs under their own steam.&lt;/p&gt;
&lt;p&gt;I am overall pleased with this idea because it pulls the rug out from under a lot of cheap, low quality &quot;web design&quot; agencies that have built their empires on the free platform for many years. I&apos;ve worked for a range of these companies and I personally would be happy to see them pivot or be made extinct.&lt;/p&gt;
&lt;p&gt;But that also means there&apos;s a lot less work for a developer to do ... so not only has WordPress been keeping developers in a cottage industry, it is also forcing people out of that industry at an increasing rate.&lt;/p&gt;
&lt;p&gt;When I looked to the horizon, my colleagues and I could see a world where WordPress was once again the de facto standard for small customers who needed a quick platform, and where agencies turn to using alternatives.&lt;/p&gt;
&lt;p&gt;I needed to skill up quickly.&lt;/p&gt;
&lt;h2&gt;Voting Pebbles &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dont-fear-jamstack/#voting-pebbles&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s still room for WordPress in the new landscape of the JAMStack world. The WP GraphQL plugin is great for sourcing content. But yes, there are a lot more alternatives that have the economies of scale and financial power to pivot much more quickly than WordPress has.&lt;/p&gt;
&lt;p&gt;A lot of progress has been made towards finding a better editor experience ... but we haven&apos;t quite solved it yet. I think projects like TinaCMS from Forestry are going to be the next iteration, and have the potential to bring an even better editing experience to users than WordPress can provide.&lt;/p&gt;
&lt;p&gt;In the meantime, developers like me will continue to push the JAMStack agenda. Why? Because we have been pushed out of our cottage industry, and we need to continue to work, and to enjoy our work. And because we do have a lot of buying power for our clients, stakeholders and family and friends whom we build for.&lt;/p&gt;
&lt;p&gt;WordPress is no longer my favourite option. But it is still an option.&lt;/p&gt;
&lt;p&gt;You started the avalanche, and it&apos;s too late for the pebbles to vote.&lt;/p&gt;
&lt;p&gt;(Wow there really is a Babylon 5 quote for every occasion.)&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Don&apos;t fear the JAMStack&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Doughnut chart SVG Animation</title><link>https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/</guid><description>For a recent project, I was asked to design an animation for a doughnut chart. The data could be dynamically editable by the content author, who could also choose how many animations to display on a given content area. </description><pubDate>Tue, 30 May 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For a recent project, I was asked to design an animation for a doughnut chart. The data could be dynamically editable by the content author, who could also choose how many animations to display on a given content area.&lt;/p&gt;
&lt;p&gt;I started off by designing the assets in Adobe Illustrator. The design required a gray background with an overlay of a specific colour that would feature the main animation. To achieve this I layered 2 circles, one on top of the other.&lt;/p&gt;
&lt;p&gt;To easily enable animating, these layers are SVG path elements. I&apos;m cheating a bit by making the top layer a linear path, instead of a true circle, but GreenSock&apos;s DrawSVG plugin works by animating the path, not the fill, of an SVG element.&lt;/p&gt;
&lt;h2&gt;The Setup &lt;a href=&quot;https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/#the-setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the initial setup I grabbed the elements and a few values, wrapping these in a jQuery each() function to isolate them from sibling animations that were occurring on the same page:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  var values = $(&apos;.statistic&apos;);

  values.each(function () {

      var percentage = $(this).find(&apos;.figure__content .fig&apos;);
      var textcontent = percentage.text();
      var circle = $(this).find(&apos;.statcircle__animated&apos;);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I already had jQuery as a dependency in this project I used the &lt;code&gt;animate()&lt;/code&gt; function to handle the text, a number that would rise from 0 to the one specified by the user.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; percentage.prop(&apos;Counter&apos;,0).animate({
      Counter: textcontent
  }, {
      duration: 1000,
      easing: &apos;swing&apos;,
      step: function (now) {
          percentage.text(Math.ceil(now));
      }
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Fallbacks &lt;a href=&quot;https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/#fallbacks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By setting the prop value to 0 means that if JavaScript isn&apos;t loaded in the browser, the percentage will still be visible to the user.&lt;/p&gt;
&lt;p&gt;I&apos;m still keen to make sure I provide a useable fallback just in case the user can&apos;t access JavaScript on their connection.&lt;/p&gt;
&lt;h2&gt;The Chart &lt;a href=&quot;https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/#the-chart&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Then, using a Tween and Draw SVG I was able to find the path, and choose an appropriate animation for the effect.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; function initTweens() {
      // animate circle path using GSAP DrawSVG
      TweenMax.fromTo(circle, 1.5, {
          // animate!
          drawSVG:&quot;0&quot;
      },
      {
          drawSVG: textcontent + &quot;%&quot;
      }); // tween
  };
  initTweens();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using fromTo allows me to set the initial value to 0 then scale up to the value I grabbed from the percentage field.&lt;/p&gt;
&lt;p&gt;I absolutely love the simplicity of DrawSVG, which meant no difficult calculations for me. I simply gave it a value and it calculated how it would draw path for me around almost 360 degrees. This gave me inherent robustness because GreenSock handles edge cases, and reduced development time greatly.&lt;/p&gt;
&lt;h2&gt;Final Result &lt;a href=&quot;https://deliciousreverie.co.uk/posts/doughnut-chart-svg-animation/#final-result&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For launch, I used Waypoints.js to specify that the animation shouldn&apos;t start until the elements were in viewport. I then created the meta fields in the back end and tied them up to the page template.&lt;/p&gt;
&lt;p&gt;You can see the final results in situ on my CodePen: https://codepen.io/endymion1818/pen/ygvVgQ&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Doughnut chart SVG Animation&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Dreamers, Builders and Cultivators</title><link>https://deliciousreverie.co.uk/posts/dreamers-builders-cultivators/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/dreamers-builders-cultivators/</guid><description>In a well established team, personality traits often come into play. Here we group three main types that, when recognised and supported, can help the business and the technology team to thrive.</description><pubDate>Fri, 09 May 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;In a well established team, personality traits come into play. Here we group three main types that, when recognised and supported, can help the business and the technology team to thrive.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Have you ever worked with a fellow engineer who doesn&apos;t seem interested in investigating new tools that are available? Have you ever met someone who annoys you because they push untested, proof-of-concept code into production?&lt;/p&gt;
&lt;p&gt;These aren&apos;t &lt;em&gt;flaws&lt;/em&gt; in our engineering team members, they&apos;re features. And when we properly understand and support them, each can be an asset to the organisation you work for.&lt;/p&gt;
&lt;p&gt;I&apos;m going to just add a caveat here: any ideas about how personalities work are just theories. We can&apos;t really understand how people think, only observe the outcome. But having observed these traits in quite a few teams, and having discussed them with other people, I think I&apos;m onto something that might be able to help you.&lt;/p&gt;
&lt;h2&gt;Personality traits&lt;/h2&gt;
&lt;p&gt;Each person on the planet has traits. Like being left handed or right handed, these are personal preferences that are deeply ingrained in all of us: although someone might work hard to use their right hand when they&apos;re left handed, they will likely always be conscious that they &lt;em&gt;prefer&lt;/em&gt; the alternative.&lt;/p&gt;
&lt;p&gt;If you watch the personalities of certain teams in your organisation, you might find certain teams share common traits. For example, I&apos;ve observed that HR teams often have common traits that have steered them towards a career in that field. I&apos;m not saying everyone on the team displays those same traits, but its likely that many will to some degree or another.&lt;/p&gt;
&lt;p&gt;And I think it&apos;s likely that you are a leader because of the traits you have.&lt;/p&gt;
&lt;p&gt;For simplicity, and to help us understand differences in individuals I&apos;ve observed within a software team, I&apos;m going to group them into three terms: &quot;dreamer&quot;, &quot;builder&quot; and &quot;cultivator&quot;.&lt;/p&gt;
&lt;p&gt;I can&apos;t take credit for identifying these groups myself: they were first outlined &lt;a href=&quot;https://www.reddit.com/r/ExperiencedDevs/comments/1ki0i60/comment/mrb9t0t/?context=3&quot;&gt;in this Reddit thread&lt;/a&gt; by user &lt;a href=&quot;https://www.reddit.com/user/j_d_q/&quot;&gt;j_d_q&lt;/a&gt;, which I&apos;m now expanding on.&lt;/p&gt;
&lt;h2&gt;The Dreamers: thinking big, not small&lt;/h2&gt;
&lt;p&gt;Dreamers think big. They&apos;re the explorers of our teams. They are constantly enthusing about new technology or new ways of doing things. You&apos;ll find them on Discord channels for Nix and Biome. They&apos;ll use Arch Linux just so they can get the latest release of packages.&lt;/p&gt;
&lt;p&gt;They are great at building enthusiasm in a team, and can help everyone keep up to date with new technology ... as long as their enthusiasm is tempered (but not smothered) with a dose of caution. Leverage their energy by getting them to build experimental new features. Give them a business problem to solve without specifying a solution. It&apos;s likely everyone on the team will be impressed with what they come up with.&lt;/p&gt;
&lt;p&gt;Try to avoid asking these people to implement a simple feature that they can do in their sleep. If you do, they&apos;ll either be asleep as they do it, or the&apos;ll find a way to do it with this obscure new package they found and want to try out.&lt;/p&gt;
&lt;p&gt;When they deliver, don&apos;t expect tested, production-ready code. Instead rely on &lt;em&gt;Builders&lt;/em&gt; to shore up their creations.&lt;/p&gt;
&lt;h2&gt;Builders: the glue that guides&lt;/h2&gt;
&lt;p&gt;Builders are great at, well, building. They&apos;ll take the raw concept that a Dreamer has come up with, adding test coverage, improving type safety and generally making sure other team members can use it easily without it breaking unexpectedly.&lt;/p&gt;
&lt;p&gt;Builders aren&apos;t focused on concepts as much as they are execution: it&apos;s more difficult for them to conjure up solutions without any guidance. If you ask them to, they could become stuck in a loop where they can&apos;t decide on which approach to implement. Assist them by encouraging them to shortlist 2 or 3 approaches, then decide with them. This will give them the confidence they need to progress.&lt;/p&gt;
&lt;p&gt;Leverage their love of documentation, clean code and automation to refine and protect code that needs attention or that is difficult to work with. Allow them to introduce coding standards and tools that free up cognitive ability for the whole team.&lt;/p&gt;
&lt;p&gt;Use builders to solve problems that cultivators cannot because they&apos;re engrossed in delivering on their current workload.&lt;/p&gt;
&lt;h2&gt;Cultivators: implementors that tend&lt;/h2&gt;
&lt;p&gt;Cultivators like structure. They see their role as tending to the land that the dreamers and builders have set up.&lt;/p&gt;
&lt;p&gt;Have a project with a large bug backlog? Give it to a cultivator and watch them steadily and happily clear it.&lt;/p&gt;
&lt;p&gt;They can take a product requirement that has a clear implementation path and be confident to carry it out. Anything well documented that has a lot of clarity, and less room for the unexpected and they will thrive.&lt;/p&gt;
&lt;p&gt;Leverage their deep knowledge of the codebase by pairing them with one of the other types. Arrange regular check-ins to make sure they&apos;re on track and don&apos;t feel blocked by something that they deem as outside the requirements of the task.&lt;/p&gt;
&lt;p&gt;Try to avoid giving these people a sketchy feature with no description and expect them to fulfil it.&lt;/p&gt;
&lt;p&gt;Cultivators are essential to carrying out tasks that builders and dreamers would struggle with.&lt;/p&gt;
&lt;h2&gt;There are no rules&lt;/h2&gt;
&lt;p&gt;At the end of the day, nobody&apos;s traits are set in stone; a left-handed person can learn to use their right hand pretty well. And traits are stronger in some people and not in others: there is a portion of the population that are ambidextrous and use both hands with equal aplomb.&lt;/p&gt;
&lt;p&gt;As I said at the start, this is just a rough outline that I think can help leaders understand and work with team members who have different strengths and abilities.&lt;/p&gt;
&lt;p&gt;We can leverage these abilities for the betterment of the whole team.&lt;/p&gt;
&lt;p&gt;If you have the capacity, identifying and grouping different engineers according to project requirements can help you to fulfil them with greater speed and efficiency.&lt;/p&gt;
&lt;p&gt;Understanding your team members this way can also help negotiate disagreements: if arguments erupt over different approaches engineers might have, you can point out respective strengths to dissenting team members. You can also work with individuals to balance their traits out a little.&lt;/p&gt;
&lt;p&gt;Pushing every engineer to have the exact same set of skills isn&apos;t going work to the best advantage of the team and could lead to some valued members withdrawing from the team or even quitting.&lt;/p&gt;
&lt;p&gt;Instead, cultivate a happier and more productive workforce by respecting differences and using them to everyone&apos;s advantage.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Dreamers, Builders and Cultivators&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Dynamic client side routes in GatsbyJS</title><link>https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/</guid><description>Would you use GatsbyJS for a dynamic app? Yes I would. The reason is that I can make good use of Gatsby&apos;s great developer experience, as well as do everything I would normally do in a React application. Here&apos;s how I recently implemented dynamic routing. </description><pubDate>Thu, 18 Jun 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Would you use GatsbyJS for a dynamic app? Yes I would. The reason is that I can make good use of Gatsby&apos;s great developer experience, as well as do everything I would normally do in a React application. Here&apos;s how I recently implemented dynamic routing.&lt;/p&gt;
&lt;p&gt;Disclaimer: My colleage Romina Moya originally discovered how Gatsby could do this, and showed it to me afterwards.&lt;/p&gt;
&lt;p&gt;I&apos;ve been learning more about Amazon Web Services recently, and found an excellent tutorial written by the makers of Seed, a sort of CI pipeline for serverless applications. You can find the tutorial at &lt;a href=&quot;https://serverless-stack.com/&quot;&gt;https://serverless-stack.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wanted to diverge from the tutorial a little on the frontend, especially since I really enjoy working with GatsbyJS and wanted to use it&apos;s static rendering where I could.&lt;/p&gt;
&lt;p&gt;One of the lessons uses React Router to render routes for notes like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/notes/2074d6b0-c5d7-11ea-bd39-5f447bbc7b39
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where the last part of the url (the &quot;pathname&quot;) needs to be generated client-side only. The reason is that the component would resolve to any number of records in the database, and needs be accessible only to the logged-in user.&lt;/p&gt;
&lt;p&gt;To achieve this in Gatsby I referred to their excellent documentation (&lt;a href=&quot;https://www.gatsbyjs.org/docs/client-only-routes-and-user-authentication/&quot;&gt;https://www.gatsbyjs.org/docs/client-only-routes-and-user-authentication/&lt;/a&gt;), but there&apos;s a lot of information in that documentation, and it can be a little difficult to see what you need to do. So here&apos;s some extra pointers that I thought might be useful:&lt;/p&gt;
&lt;h2&gt;Setup &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/#setup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In my applications as in most Gatsby sites, I use a wrapping component that I name &quot;layout&quot; or &quot;entry&quot;. If you don&apos;t have that, you could effectively do the same thing by adding a file in the root of your project named &quot;gatsby-browser.js&quot; and using the onClientEntry() api (&lt;a href=&quot;https://www.gatsbyjs.org/docs/browser-apis/#onClientEntry&quot;&gt;https://www.gatsbyjs.org/docs/browser-apis/#onClientEntry&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;In that file I needed to first import { Router } from &quot;@reach-router&quot;, and also import { navigate } from &quot;gatsby&quot; then in the render function add this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Router&amp;gt;
  &amp;lt;BounceToHome default /&amp;gt;
&amp;lt;/Router&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The BounceToHome function is for any path the router comes across that isn&apos;t defined (as you can see from the default prop I passed to it). This is useful for if the user types anything That function uses navigate() that I imported earlier:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const BounceToHome = () =&amp;gt; {
  useEffect(() =&amp;gt; {
    navigate(&quot;/&quot;, { replace: true });
  }, []);
  return null;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see I use the useEffect hook to navigate the user home on the first render.&lt;/p&gt;
&lt;h2&gt;Rendering client-only component &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/#rendering-client-only-component&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At the top of the file I&apos;ve imported the functionality I already defined for rendering, editing and deleting notes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Notes from &quot;./Notes&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now as a child of the Router component I&apos;m going to add in my Notes component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Notes path=&quot;/notes/:noteId/&quot; component={Notes} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The path tells Reach Router that I want to accept paths with a prefix &quot;/notes&quot;, and the colon after that is a variable that will be passed to your component. It could be called anything you like, I thought noteId was the most relevant name for what I was building.&lt;/p&gt;
&lt;p&gt;On the following line I&apos;ve passed to the router which component I want to render on that path.&lt;/p&gt;
&lt;p&gt;Now I can use that component in the page in this way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default function Notes({noteId}) {
  useEffect(() =&amp;gt; {
    function loadNote() {
      return get(&quot;notes&quot;, `/notes/${noteId}`, &apos;&apos;);
    }
    async function onLoad() {
      try {
        const note = await loadNote();
        setContent(note.content);
        setNote(note);
      } catch (error) {
        onError(error);
      }
    }
    onLoad();
  }, [noteId]);
  return(
    ...
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever the noteId changes the useEffect hook runs and loadNote() gets the note using functionality available in the aws-amplifypackage.&lt;/p&gt;
&lt;h2&gt;Gatsby is ready for Apps! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/#gatsby-is-ready-for-apps!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If React is &quot;just JavaScript&quot;, then Gatsby is &quot;just a javascript framework&quot;, with all of the benefits that brings you, as well as some significant other things like better accessibility, static rendering, and loads of other cool stuff.&lt;/p&gt;
&lt;p&gt;Let&apos;s not be too quick to pigeon-hole Gatsby into a certain corner: it&apos;s a versatile set of tools that allows us to jump start our projects and create any number of really cool things.&lt;/p&gt;
&lt;h2&gt;Update: don&apos;t put this in your Layout / Entry file! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/dynamic-client-routes-in-gatsbyjs/#update:-don&apos;t-put-this-in-your-layout-entry-file!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After playing with this a little more, I realised that client only routes override file routes ... so if you have the dynamic routing on the root path (usually &quot;/&quot;), it&apos;ll override everything else and you won&apos;t be able to navigate to any other page you have defined in the pages folder.&lt;/p&gt;
&lt;p&gt;So it&apos;s best to scope your client routes to a folder (such as app), and avoid overriding your other pages!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Dynamic client side routes in GatsbyJS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Some of my favourite books</title><link>https://deliciousreverie.co.uk/posts/favourite-books/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/favourite-books/</guid><description>I was asked to list some of my favourite books recently, so I compiled the following list, which is very loose and still might be missing some of my favourites! </description><pubDate>Sat, 27 Jun 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I have found it very difficult to collect this list together, I go through phases with books and these might be considered my current favourites ... but here goes!&lt;/p&gt;
&lt;p&gt;In no strict order of preference:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;F Scott Fitzgerald - collected short stories. Life in the 1920s &amp;amp; 30s with just a pinch of melancholy&lt;/li&gt;
&lt;li&gt;John Keats complete poems - the sheer genius of this 20 year olds ideas was amazing. Such a tragic life too (he died aged 26)&lt;/li&gt;
&lt;li&gt;Victor Hugo - les Miserables (translated by Norman Denny). An undiluted masterpiece, Paris in minature and a lot of fascinating philosophical meandering in between.&lt;/li&gt;
&lt;li&gt;James Joyce - Poems &amp;amp; shorter writings. JJ’s books are unnaproachable but he had a command of English like only Shakespeare, which makes his poetry magnificent.&lt;/li&gt;
&lt;li&gt;Virginia Woolf - The Waves. Just read the opening page. She understood people on such an intrinsic level.&lt;/li&gt;
&lt;li&gt;Marcel Proust - Swanns Way. He takes a long time to get to the point but it’s like climbing a mountain, the view from the peak is indescribably beautiful.&lt;/li&gt;
&lt;li&gt;Foundation - Asimov. The scope of the initial concept for the series is mind boggling.&lt;/li&gt;
&lt;li&gt;Frank Herbert - Dune. world building at its absolute best&lt;/li&gt;
&lt;li&gt;Dan Simmons - The Hyperion Cantos. Requires a bit of a strong stomach but the author loved literature as well as high concept sci fi. Awesome.&lt;/li&gt;
&lt;li&gt;China Mieville - Perdido Street Station, Embassytown or The City and the City. Some incredible concepts about language and world building, writing style is mesmerising and transports you into the his worlds with ease.&lt;/li&gt;
&lt;li&gt;Mervyn Peake - Titus Groan. Brilliant, ponderous, odd, and wonderful.&lt;/li&gt;
&lt;li&gt;Jorge Luis Borges - Collected Stories. Mind bending infinite and looping concepts.&lt;/li&gt;
&lt;li&gt;Anne Fadiman - Confessions of a Common Reader. I re-read this slim volume every few years, a wonderful love letter to books, reading, and life in general.&lt;/li&gt;
&lt;li&gt;Boris Pasternak - Doctor Zhivago. a grand, poetic, and empathic exploration of individuals in Russia in the civil war. &quot;I feel for each of them / As if I were in their skin&quot;, he writes in one of the poems attached to the book.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Honourable mentions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/favourite-books/#honourable-mentions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I am halfway through the Illiad, an old translation by a guy called Chapman who kept the original meter from the poem, it’s jolly wordy and doesn’t convey the barbarism of the original (probably a good thing!).&lt;/p&gt;
&lt;p&gt;I also recently read Paradise Lost by John Milton. The charachterization is superb and language and turn of phrase is delightful.&lt;/p&gt;
&lt;p&gt;Sophie’s World is good for covering the basic concepts of just about every branch of philosophy, in story form too!&lt;/p&gt;
&lt;p&gt;I read Robinson Crusoe but the racism and colonialism were stomach churning.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Some of my favourite books&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First Steps with CSS Animations</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/</guid><description>CSS Animations are quickly complimenting if not replacing Javascript for generating a little bit of on-page whizz (that&apos;s the technical term). This is the first time I&apos;ve delved into the syntax for CSS Animations that I hope can replace the tired carousels resident on many websites. Here&apos;s how I implemented it.</description><pubDate>Tue, 14 Apr 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;CSS Animations are quickly complimenting if not replacing Javascript for generating a little bit of on-page whizz (that&apos;s the technical term). This is the first time I&apos;ve delved into the syntax for CSS Animations that I hope can replace the tired carousels resident on many websites. Here&apos;s how I implemented it.&lt;/p&gt;
&lt;p&gt;First of all, I&apos;ve stuck to using CSS for this project although I&apos;m using SASS for this site. The reason is that I wanted to get familiar with the CSS syntax first. I don&apos;t want to chain myself to SASS because it&apos;s one step removed from what the browser renders. Anyway, that&apos;s a story for another day.&lt;/p&gt;
&lt;h3&gt;Step 1: Scope the Animation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#step-1:-scope-the-animation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Being a small marketing agency we&apos;re still formulating the best way to approach website builds. So this project came to me with a visual design but no scope. The designs showed what was required, that the circular images would change to reveal text on flat background.&lt;/p&gt;
&lt;p&gt;What they weren&apos;t so good at displaying is the end state of the animation. Would it roll around again? The plan was originally for a Javascript carousel here, so I could make some assumptions about the functionality based on that.&lt;/p&gt;
&lt;h3&gt;Step 2: The HTML &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#step-2:-the-html&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;container&quot;&amp;gt;
    &amp;lt;a href=&quot;services.php&quot;&amp;gt;
      &amp;lt;div class=&quot;col-sm-3 home-circle&quot; id=&quot;one&quot;&amp;gt;
          &amp;lt;img class=&quot;bottom&quot; id=&quot;five&quot; src=&quot;/assets/img/property1.png&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/a&amp;gt;
    &amp;lt;a href=&quot;services.php&quot;&amp;gt;
      &amp;lt;div class=&quot;col-sm-3 home-circle&quot; id=&quot;two&quot;&amp;gt;
        &amp;lt;img class=&quot;bottom&quot; id=&quot;six&quot; src=&quot;/assets/img/property2.png&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/a&amp;gt;
    &amp;lt;a href=&quot;services.php&quot;&amp;gt;
      &amp;lt;div class=&quot;col-sm-3 home-circle&quot; id=&quot;three&quot;&amp;gt;
        &amp;lt;img class=&quot;bottom&quot; id=&quot;seven&quot; src=&quot;/assets/img/property3.png&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/a&amp;gt;
    &amp;lt;a href=&quot;services.php&quot;&amp;gt;
      &amp;lt;div class=&quot;col-sm-3 home-circle&quot; id=&quot;four&quot;&amp;gt;
        &amp;lt;img class=&quot;bottom&quot; id=&quot;eight&quot; src=&quot;/assets/img/property4.png&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/a&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This took me a few attempts to work out. I try to avoid using position:absolute wherever possible, so in this case I used a background-imageon the parent div with an img inside.&lt;/p&gt;
&lt;h3&gt;Step 3: Declaring the First Animation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#step-3:-declaring-the-first-animation&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
@-webkit-keyframes fadeOut { from { opacity:1; } to { opacity:0; } }
@-moz-keyframes fadeOut { from { opacity:1; } to { opacity:0; } }
@keyframes fadeOut { from { opacity:1; } to { opacity:0; } }
@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } }
@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }

.home-banner .container a .col-sm-3 {
   opacity:0;
   -webkit-animation:fadeIn ease-in 1;
   -moz-animation:fadeIn ease-in 1;
   animation:fadeIn ease-in 1;

   -webkit-animation-fill-mode:forwards;
   -moz-animation-fill-mode:forwards;
   animation-fill-mode:forwards;

   -webkit-animation-duration:1s;
   -moz-animation-duration:1s;
   animation-duration:1s;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because I&apos;m a visually-minded person, I decided to break my CSS into two parts and to declare the animations in areas that would help me logically follow what was displaying on the site, even if it would be slightly less efficient. I&apos;m sure that as I get more used to the syntax I&apos;ll change this practice to be as efficient as possible (and to use SASS).&lt;/p&gt;
&lt;h3&gt;Step 4: Timing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#step-4:-timing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve heard it said a few times that timing is the hardest thing to get right in an animation. It was quite difficult to work out the gaps that their should be between each animation state, and took quite a few goes before it appeared to flow smoothly, almost as if there was a short increase in speed in the middle two animations, although this is just a perception.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.home-banner #one {
  background:url(&apos;/assets/img/propcons.svg&apos;) center no-repeat;
  background-size: 100%;

  -webkit-animation-delay: 0.4s;
  -moz-animation-delay: 0.4s;
  animation-delay: 0.4s;
}
.home-banner #two {
  background:url(&apos;/assets/img/charsurv.svg&apos;) center no-repeat;
  background-size: 100%;

  -webkit-animation-delay: 0.8s;
  -moz-animation-delay: 0.8s;
  animation-delay: 0.8s;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I was very conscious of was that the delay between the first animation (the property images) appearing, and the second (the text overlays) needed to be a) slow enough so the client felt they had good value out of the images we had provided and b) fast enough that visitors would see the second transition before they scrolled further down the page.&lt;/p&gt;
&lt;p&gt;So we settled on .6 seconds, which seemed to be the optimum delay we could achieve without investing in further research.&lt;/p&gt;
&lt;p&gt;Here&apos;s the result (sorry for the high compression - wanted to keep a smaller file size as possible!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Browser Caveats &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#browser-caveats&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What we noticed going through the testing phase was that certain aspects of the animation spec are still under development. Some things like animation-reverse aren&apos;t supported in Vivaldi or Mobile Chrome (tested on iOS 8). But we felt that since the animation still works to a fair degree, it would be reasonable to keep using it.&lt;/p&gt;
&lt;h3&gt;CSS Animations - The Way Forward &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-css-animations/#css-animations-the-way-forward&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I got a huge amount of satisfaction trying out the CSS Animation spec. This isn&apos;t only due to my lack of JavaScript skills (which are steadily improving) but also my desire to build high quality websites that are as lightweight as possible.&lt;/p&gt;
&lt;p&gt;The outcome was so well recieved by our team involved that it&apos;s opened our designers and other stakeholders eyes to the options available to them:- they now have many, many more options at their fingertips instead of only the aged and worn carousel default.&lt;/p&gt;
&lt;p&gt;This demonstrated for me that even though it is often difficult to step out of our comfort zone, but the rewards of doing so are often great.&lt;/p&gt;
&lt;p&gt;I&apos;d love to know how you would improve on this code, or if you&apos;ve used CSS Animations in a way that has challenged you recently.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First Steps with CSS Animations&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First Steps with Flexbox</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-flexbox/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-flexbox/</guid><description>On a recent project for international research machine builder Anton Paar, I used the new flex-box CSS module for the first time. Here&apos;s how it went.</description><pubDate>Sun, 15 Feb 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;On a recent project for international research machine builder Anton Paar, I used the new flex-box CSS module for the first time. Here&apos;s how it went.&lt;/p&gt;
&lt;p&gt;The flex box module has had a long gestation period, and there are 2 main specs of the module, so when researching methods of using it you have to be careful to find recent enough posts which outline the latest spec.&lt;/p&gt;
&lt;p&gt;As always, &lt;a href=&quot;https://css-tricks.com/snippets/css/a-guide-to-flexbox/&quot;&gt;CSS Tricks&lt;/a&gt; was particularly useful, along with &lt;a href=&quot;https://stackoverflow.com/questions/tagged/flexbox&quot;&gt;Stack Overflow&lt;/a&gt; in getting familiar with the syntax and methods.&lt;/p&gt;
&lt;h3&gt;The Problem &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-flexbox/#the-problem&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&apos;s not a biggie, but I like things to line up nicely. As it stood, the four content boxes on the home page of Anton Paar UK&apos;s microsite would be populated by both static content and dynamic. We decided to use Flexbox to line the four columns up, something that has never been achieveable before in CSS without Javascript.&lt;/p&gt;
&lt;p&gt;Here&apos;s the HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;row home-features&quot;&amp;gt;
	&amp;lt;div class=&quot;col-md-3 home-feature&quot;&amp;gt;
		&amp;lt;div class=&quot;well&quot;&amp;gt;
			&amp;lt;?php
			if ( function_exists( &apos;dynamic_sidebar&apos; ) ) {
				dynamic_sidebar( &quot;home-1&quot; );
		} ?&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&quot;col-md-3 home-feature&quot;&amp;gt;
		&amp;lt;div class=&quot;well&quot;&amp;gt;
			&amp;lt;?php
			if ( function_exists( &apos;dynamic_sidebar&apos; ) ) {
				dynamic_sidebar( &quot;home-2&quot; );
			} ?&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&quot;col-md-3 home-feature&quot;&amp;gt;
		&amp;lt;div class=&quot;well&quot;&amp;gt;
			&amp;lt;?php
			if ( function_exists( &apos;dynamic_sidebar&apos; ) ) {
				dynamic_sidebar( &quot;home-3&quot; );
			} ?&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
	&amp;lt;div class=&quot;col-md-3 home-feature&quot;&amp;gt;
		&amp;lt;div class=&quot;well&quot;&amp;gt;
			&amp;lt;?php
			if ( function_exists( &apos;dynamic_sidebar&apos; ) ) {
				dynamic_sidebar( &quot;home-4&quot; );
			} ?&amp;gt;
			&amp;lt;/div&amp;gt;
		&amp;lt;/div&amp;gt;
	&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see from the following screenshot from before the site went live, the problem was that the 4 columns weren&apos;t lining up. This has long been almost impossible to do in HTML / CSS, certainly without misappropriating the position: absolute property.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;But using Flexbox allowed us to simplify the code by removing the wellDIVs as well as provide a solution to the problem:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.home-features {
  display: -webkit-box;
  display: -moz-box;
  display: -webkit-flexbox;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
  padding: 0 10px;
}
.home-feature {
  -webkit-box-flex: 1;
  -moz-box-flex: 1;
  -webkit-flex: 1;
  -ms-flex: 1;
  flex: 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added this attribute under a media-query of (min-width: 992px)so that the columns would stack at smaller viewport sizes.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This exercise has proved to be a great solution to an ongoing problem in web development.&lt;/p&gt;
&lt;p&gt;After using it on this project I really don&apos;t want to go back, which is a shame because there are still some caveats with Safari (Both Mac OS X and iOS).&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First Steps with Flexbox&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First steps with the Temporal API</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-js-temporal/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-js-temporal/</guid><description>Dates in JavaScript are changing forever: The Temporal API is now at Stage 4, which means it&apos;s stable, and there&apos;s a polyfill available. It&apos;s time to start getting familiar with the API.</description><pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Dates in JavaScript are changing forever: The Temporal API is now at Stage 4, which means it&apos;s stable, and there&apos;s a polyfill available. It&apos;s time to start getting familiar with the API.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One thing I do a lot of is converting dates that are stored using Epoch dates. These are numbers that represent every millisecond since 1 January 1970. At the time of writing, the digit for the current epoch date was 1746706628000. A second later it was 1746706629000. And so on.&lt;/p&gt;
&lt;p&gt;If you&apos;d like to know more about epoch timestamps, this is a good resource: https://www.epochconverter.com/&lt;/p&gt;
&lt;p&gt;These are ideal for storing data, but we&apos;ve always had issues calculating these into dates in our applications because of time zones and locations.&lt;/p&gt;
&lt;p&gt;Temporal is much better because you don&apos;t need to load extra JavaScript that you&apos;d ordinarily have to get from a package: one of the most popular is moment.js.&lt;/p&gt;
&lt;p&gt;But these packages tend to be fairly large in size. You could roll your own functions but ... they tend to be challenging reason about and difficult to maintain.&lt;/p&gt;
&lt;h2&gt;Polyfilling&lt;/h2&gt;
&lt;p&gt;There are &lt;a href=&quot;https://www.npmjs.com/search?q=temporal%20polyfill&quot;&gt;two spec-compliant polyfills&lt;/a&gt; for Temporal, one of which I&apos;m using until browsers are updated, at which point it should be just a case or removing the import.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { Temporal } from &apos;@js-temporal/polyfill&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Converting Epoch timestamps using Temporal&lt;/h2&gt;
&lt;p&gt;The first solution I needed was to convert that epoch date mentioned above into a datetime object so that I can then localise it.&lt;/p&gt;
&lt;p&gt;Temporal has that easily covered:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const timeInstant = Temporal.Instant.fromEpochMilliseconds(1746706629000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you log this out you get a new &lt;code&gt;Temporal.Instant&lt;/code&gt; type with the milliseconds and nanoseconds properties. Not useful at this point but an important step nonetheless. It&apos;s now a Temporal instance, much like &lt;code&gt;new Date(mydate)&lt;/code&gt; converted a string into a Date.&lt;/p&gt;
&lt;p&gt;But what if your Epoch date uses &lt;em&gt;seconds&lt;/em&gt; instead of milliseconds? &lt;code&gt;Temporal.fromEpochSeconds()&lt;/code&gt; doesn&apos;t exist, so you need to manually turn the number into milliseconds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const timeInstant = Temporal.Instant.fromEpochMilliseconds(1746706629 * 1000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we&apos;ve got a Temporal instance, the next step is to convert this into a date that is human-readable and localised.&lt;/p&gt;
&lt;h2&gt;Localising&lt;/h2&gt;
&lt;p&gt;To localise a date we need to do three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set a format to present the date&lt;/li&gt;
&lt;li&gt;Set a &lt;code&gt;locale&lt;/code&gt; that can be used to arrange the dates (this will determine, for example, if it&apos;s DD/MM/YYYY, or MM/DD/YYYY which is US style.)&lt;/li&gt;
&lt;li&gt;Set the local time so that it reflects the users time, accounting for daylight savings etc&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const localisedDate = timeInstant.toLocaleString(
  &apos;en-GB&apos;,
  {
    timeZone: &apos;Europe/London&apos;,
    day: &apos;2-digit&apos;,
    month: &apos;short&apos;,
    year: &apos;numeric&apos;
  }
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Temporal is going to save you time&lt;/h2&gt;
&lt;p&gt;Hah, see what I did there. But yes, kilobytes of extra JavaScript gone from your projects. A standard way to modify dates. And no more wrangling with &lt;code&gt;Date()&lt;/code&gt;. That&apos;s what you can look forward to with the Temporal API.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First steps with the Temporal API&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First Steps with Story Animations</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/</guid><description>For a while now I&apos;ve really enjoyed tinkering with animations as a way to bring a lot more quality to my projects. But as well as providing interactivity, I was curious to explore another way animations can be used in a project: to tell a story. </description><pubDate>Sat, 15 Jul 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For a while now I&apos;ve really enjoyed tinkering with animations as a way to bring a lot more quality to my projects. But as well as providing interactivity, I was curious to explore another way animations can be used in a project: to tell a story.&lt;/p&gt;
&lt;p&gt;I&apos;ve discovered that animations can really make a project stand out from the competition, and I find myself driven to this field more and more. Partly, I want to do this to showcase what the web is really capable of in difference to other mediums. But, being practical about it, it&apos;s also a great way of getting users to stay on your site more and for it to be memorable.&lt;/p&gt;
&lt;p&gt;The style I adopted for this site was heavily influenced by my love of the English and Welsh countryside. I&apos;ve also been thinking a lot about the animation film &quot;Watership Down&quot; lately. I watched that film as a kid and it seems to have made a lasting impression on me. So I took the idea from those influences.&lt;/p&gt;
&lt;h2&gt;Drawing the SVGs &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/#drawing-the-svgs&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used both Affinity Designer and Sublime Text to manipulate some SVGs I shamefully got free from the internet. I&apos;m sorry, I&apos;m not proud of that fact, but time was not on my side.&lt;/p&gt;
&lt;p&gt;Although you percieve there to be one tree that transitions from winter to summer, and one rabbit that moves across from one side of the screen to the other, there are actually 2 trees and 8 rabbits.&lt;/p&gt;
&lt;p&gt;This is because the easiest way to create the perception of movement I have found is to change the opacity.&lt;/p&gt;
&lt;h2&gt;Animating &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/#animating&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Each element is changed by modifying the opacity in GreenSock Animation Platform, which is tied to ScrollMagic, in order to perform the animations not on a timeline, but on scroll.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;window.onload = function(){

  // smController = new ScrollMagic.Controller();
  smTreeController = new ScrollMagic.Controller();
  smRabbitController = new ScrollMagic.Controller();

  // getters
  var trigger = document.getElementById(&apos;main&apos;);

  var treeSummer = document.getElementById(&apos;tree-summer&apos;);
  var treeWinter = document.getElementById(&apos;tree-winter&apos;);

  var rabbit0 = document.getElementById(&apos;rabbit-0&apos;);
  var rabbit1 = document.getElementById(&apos;rabbit-1&apos;);
  var rabbit3 = document.getElementById(&apos;rabbit-3&apos;);
  var rabbit4 = document.getElementById(&apos;rabbit-4&apos;);
  var rabbit5 = document.getElementById(&apos;rabbit-5&apos;);
  var rabbit6 = document.getElementById(&apos;rabbit-6&apos;);
  var rabbit8 = document.getElementById(&apos;rabbit-8&apos;);

  // from winter to summer
  var treeWinterToSummer1 = new ScrollMagic.Scene({
    triggerElement: trigger,
    duration: &apos;100%&apos;,
    offset: &apos;400%&apos;
  })
  .setTween(treeSummer, { opacity: 0.6 })
  .addTo(smTreeController);

  var treeWinterToSummer2 = new ScrollMagic.Scene({
    triggerElement: trigger,
    duration: &apos;100%&apos;,
    offset: &apos;500%&apos;
  })
  .setTween(treeWinter, { opacity: 0 })
  .addTo(smTreeController);

  // 0
  var heyRabbit0 = new ScrollMagic.Scene({
    triggerElement: trigger,
    offset: &apos;500%&apos;,
    duration: &apos;15%&apos;
  })
  .setTween(rabbit0, { opacity: 0.6 })
  .addTo(smRabbitController);

  var byeRabbit0 = new ScrollMagic.Scene({
    triggerElement: trigger,
    offset: &apos;600%&apos;,
    duration: &apos;15%&apos;
  })
  .setTween(rabbit0, { opacity: 0 })
  .addTo(smRabbitController);

  // 1
  var heyRabbit1 = new ScrollMagic.Scene({
    triggerElement: trigger,
    offset: &apos;650%&apos;,
    duration: &apos;15%&apos;
  })
  .setTween(rabbit1, { opacity: 0.6 })
  .addTo(smRabbitController);

  var byeRabbit1 = new ScrollMagic.Scene({
    triggerElement: trigger,
    offset: &apos;750%&apos;,
    duration: &apos;15%&apos;
  })
  .setTween(rabbit1, { opacity: 0 })
  .addTo(smRabbitController);

// and so on...

};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There were actually 2 versions of this animation. I played with greensock to tween the appearance and disappearance of the rabbits. However, I decided against this approach beacuse in order to make the last-but-one rabbit stay in view for longer, I would have to set a pin, and release it later.&lt;/p&gt;
&lt;p&gt;I didn&apos;t think this approach was robust enough, especially since I want to build on this animation in the future. Having lots of spurious divs positioned around my dom didn&apos;t feel right.&lt;/p&gt;
&lt;h2&gt;Performance Considerations &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/#performance-considerations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Loading up these libraries does have a performance cost of course. A cost that is most noticeable on mobile devices. So instead of enqueueing my Javascript in the normal way, I&apos;ve been working on a script that writes the tag into the DOM once you&apos;re at a certain breakpoint:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (&apos;querySelector&apos; in document &amp;amp;&amp;amp; &apos;addEventListener&apos; in window) {
    var windowWidth = document.body.clientWidth;
    if(windowWidth &amp;gt; 1024){

        (function(H){H.className=H.className.replace(/\bno-js\b/,&apos;js&apos;)})(document.documentElement);

        document.write(unescape(&quot;%3Cscript src=&apos;/js/deliciousreverie-noncriticalscripts.min.js&apos; type=&apos;text/javascript&apos; defer%3E%3C/script%3E&quot;));
        }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I further scoped this hit by adding a partial to the footer in hugo, so that the partial containing the SVG would only load on the home page.&lt;/p&gt;
&lt;p&gt;The home page loading time hovers around 3 seconds on desktop now. I know it could be better, but frankly, I feel the hit is worth it for something that I think will add plenty of value to my site.&lt;/p&gt;
&lt;h2&gt;Accessibility &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/#accessibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I don&apos; think there are accessibility concerns with this project. The animation is purely illustrative and shouldn&apos;t affect screen readers or sight impaired ... I hope!&lt;/p&gt;
&lt;p&gt;Please don&apos;t hesitate to let me know if it does.&lt;/p&gt;
&lt;h2&gt;Future Plans &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-story-animations/#future-plans&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I already have plans for similarly-themed animations on other pages on the site, and have started building a bird that will fly across the screen, circle near the tree, and disappear off-screen.&lt;/p&gt;
&lt;p&gt;These I&apos;ll hopefully get done before the kids leave home.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First Steps with Story Animations&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First steps with Sveltekit</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/</guid><description>SvelteKit is great for one reason: performance. And it&apos;s this above everything else (except accessibility) that I&apos;m excited about.</description><pubDate>Thu, 27 May 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Svelte is a new framework for JavaScript ... hear me out! It does seem to some people that there&apos;s a new JavaScript framework out every day. And there are a lot of new ones at the minute. But SvelteKit is great for one reason: performance. And it&apos;s this above everything else (except accessibility) that I&apos;m excited about.&lt;/p&gt;
&lt;p&gt;Svelte does have a lot of things going for it. It&apos;s as nice (if not nicer) to develop with than React, and getting started is a lot, lot quicker.&lt;/p&gt;
&lt;p&gt;These days, if you&apos;re going to build a site with React, you either go with the slightly walled garden of create-react-app, or something like Next or Gatsby. Vue has it&apos;s own set of these problems too.&lt;/p&gt;
&lt;p&gt;However Svelte ... isn&apos;t just a framework. It&apos;s a compiler too. This facilitates much smaller bundle sizes:&lt;/p&gt;
&lt;p&gt;React is 42.2kb including react-dom. By comparison, Preact is 4kb. By contrast, Svelte is 1.6kb. That blows my mind.&lt;/p&gt;
&lt;p&gt;In our day of multi-megabyte downloads for some websites (that I&apos;ve seen), Svelte could really turn the tide.&lt;/p&gt;
&lt;h2&gt;Developer experience &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/#developer-experience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The other thing I want to talk about is the developer experience of Svelte. It&apos;s very pleasant to work with. Writing components feels a lot more like writing native HTML and CSS. There were certainly some unusual (to me) augmentations, for example writing conditional blocks has an unusual syntax, with { #if } and { /if} either side of the condition.&lt;/p&gt;
&lt;p&gt;I have to say, I do kinda prefer React&apos;s slightly more JavaScript-y approach.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{condition &amp;amp;&amp;amp; &amp;lt;div&amp;gt;conditional item&amp;lt;/div&amp;gt;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, when it comes to rendering HTML content, it&apos;s much more succinct.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{@html htmlContent}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don&apos;t get me wrong, that reminder is pretty useful. However once you&apos;re familiar with the concept, it&apos;s not exactly a big deal any more.&lt;/p&gt;
&lt;h2&gt;SvelteKit vs NextJS vs Gatsby &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/#sveltekit-vs-nextjs-vs-gatsby&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SvelteKit is definitely going more the route of NextJS rather than Gatsby. Gatsby is a little more prescriptive and great for newer developers (or those short on time), but NextJS allows much more freedom.&lt;/p&gt;
&lt;p&gt;Similar to NextJS, SvelteKit will have options for rendering with SSG / SSR or on the client, and different adapters for interfacing with serverless functions from different providers.&lt;/p&gt;
&lt;p&gt;This means you have to fetch data your own way, either on the client or server, and that means you have to be a bit more aware of the performance costs involved.&lt;/p&gt;
&lt;p&gt;However, I couldn&apos;t find any reference to how you build a NextJS that compiles at build time, that doesn&apos;t send any JavaScript to the client. Yet, that&apos;s one of the excellent things about SvelteKit: I easily configured my site to send exactly no clientside javascript to the browser, and it works perfectly!&lt;/p&gt;
&lt;h2&gt;My project &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/#my-project&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can take a look at my live project at &lt;a href=&quot;https://promatt.co.uk/&quot;&gt;https://promatt.co.uk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I wrote this small site using a starter built by &lt;a href=&quot;https://scottspence.com/posts/graphcms-svelte-starter&quot;&gt;Scott Spence&lt;/a&gt;. It fetches data from [Hygraph](: &lt;a href=&quot;https://hygraph.com/&quot;&gt;https://hygraph.com/&lt;/a&gt;) and is hosted on &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;. I&apos;ve used &lt;a href=&quot;https://formspree.io/&quot;&gt;Formspree&lt;/a&gt; to forward emails from the /contact page to my friend&apos;s email address, but I could have just as easily done that with a serverless function.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-steps-with-sveltekit/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s one other project I&apos;ve got my eye on that will trump SvelteKit for my tool of choice. I think it&apos;s a great challenge to other frameworks, and I really hope it starts some kind of &quot;performance war&quot; where the performance costs of frameworks are driven down by competition in the space.&lt;/p&gt;
&lt;p&gt;Even if that doesn&apos;t happen, SvelteKit is a wonderful addition to the JavaScript developers&apos; tool belt.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First steps with Sveltekit&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First Steps with zsh</title><link>https://deliciousreverie.co.uk/posts/first-steps-with-zsh/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-steps-with-zsh/</guid><description>I&apos;m one of those guys that was terrified of the command line. Literally, terrified. I knew enough that simple commands could irreparably break my computer, and that scared me. But gradually, I&apos;ve begun to use more command line tools, and recently discovered ways to make my workflow easier by using zsh terminal commands. </description><pubDate>Wed, 24 Aug 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m one of those guys (coming from a design background) that was terrified of the command line. Literally, terrified. I knew enough that simple commands could irreparably break my computer, and that scared me. But gradually, I&apos;ve begun to use more command line tools, and recently discovered ways to make my workflow easier by using zsh terminal commands.&lt;/p&gt;
&lt;p&gt;I started using the terminal because of the benefits of build tools. I use Grunt in everyday production of my websites now, to minify &amp;amp; concat my JS and SASS files, and sometimes to minify images and SVGs. If it hadn&apos;t been for these benefits, I doubt I would have ever used the terminal in the way I do now.&lt;/p&gt;
&lt;h2&gt;GIT in its Native Environment&lt;/h2&gt;
&lt;p&gt;I recently ditched my 2 git graphical user interface programs (Github and Git Kraken) in favour of using the terminal. I liked how I could have more control over my commits this way, and not have the extra overhead of another program open whilst I&apos;m trying to work (I have a very minimalist approach to working, as far as that&apos;s possible).&lt;/p&gt;
&lt;p&gt;Git on the command line is fantastically useful, and I&apos;ve never been more comfortable stashing, switching branches and doing all kinds of fancy things ... but I always wanted my terminal to do something more.&lt;/p&gt;
&lt;p&gt;I find the following things really useful: This is the time I ran the command. So if I&apos;m working through a project I can see exactly what time something was done ... this gets useful if you&apos;re tracking how long a certain task has taken, especially if your time is billable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/S/ech-ech
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The next bit shows the current path in an abbreviated form. For example, the /S/ refers to the &quot;Sites&quot; directory.&lt;/p&gt;
&lt;p&gt;Following that is the git information I was telling you about:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git:(master)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This shows that the current directory is a git project, and that the current branch is one called master.&lt;/p&gt;
&lt;p&gt;This reference, in purple in the screenshot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0dd6b659
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Is the commit hash. I use this to track my commits through the day, and can easily scan up &amp;amp; down my commit history to see what changes are made, when.&lt;/p&gt;
&lt;h2&gt;A Note about Tools&lt;/h2&gt;
&lt;p&gt;To get this functionality, I&apos;m using a tool called &lt;a href=&quot;https://ohmyz.sh/&quot;&gt;Oh My zsh&lt;/a&gt;, a tool which has hundreds of themes , of which I&apos;m using this one: &lt;a href=&quot;https://github.com/calebmeyer/cpm-zsh-theme&quot;&gt;https://github.com/calebmeyer/cpm-zsh-theme&lt;/a&gt;, which you have to install manually (instructions are on the readme file).&lt;/p&gt;
&lt;p&gt;I also enabled a few extra plugins for git functionality by adding this to the .zshrc file: plugins=(git git-extras git-remote-branch)&lt;/p&gt;
&lt;p&gt;I would never have become this confident were it not for this course on Udemy: &lt;a href=&quot;https://thoughtbot.com/upcase/videos/git-workflow&quot;&gt;https://thoughtbot.com/upcase/videos/git-workflow&lt;/a&gt;. Check it out! There&apos;s lots more in the course, including some snippets you can run so that you can use git even more effectively.&lt;/p&gt;
&lt;h2&gt;Confidence in Progress&lt;/h2&gt;
&lt;p&gt;I&apos;m still working on using the command line effectively. I&apos;ve recently decided to start using webp images in my website builds ... there&apos;s a command-line tool that &lt;a href=&quot;https://developers.google.com/speed/webp/docs/cwebp&quot;&gt;converts jpg files to webp&lt;/a&gt; and a script here that allows you batch process all images in a directory: &lt;a href=&quot;https://stackoverflow.com/questions/26565191/batch-process-png-to-webp&quot;&gt;https://stackoverflow.com/questions/26565191/batch-process-png-to-webp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So, if you&apos;re not confident with the command line, there are many useful, time saving things you can do. Perhaps now is the time to give it a go?&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First Steps with zsh&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>First 2 weeks at Webiny</title><link>https://deliciousreverie.co.uk/posts/first-two-weeks-at-webiny/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/first-two-weeks-at-webiny/</guid><description>I&apos;ve been at Webiny two weeks now. I have had the most fun I&apos;ve had in a long time career wise. Here&apos;s what I&apos;ve learned and what my objectives are going to be for the coming few weeks. </description><pubDate>Fri, 11 Mar 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve been at Webiny two weeks now. I have had the most fun I&apos;ve had in a long time career wise. Here&apos;s what I&apos;ve learned and what my objectives are going to be for the coming few weeks.&lt;/p&gt;
&lt;p&gt;The first week at any company is always difficult. There&apos;s so much to learn, there&apos;s a new culture to adopt, and new personal dynamics to interact with. First steps always falter. But now, two weeks in, I&apos;m beginning to feel more confident. I&apos;ve made some contributions that I&apos;m happy with, and I&apos;m already feeling more settled with my decision to move.&lt;/p&gt;
&lt;p&gt;I don&apos;t quite know what I was expecting from Webiny. I already knew the people here a little, and the reason I joined was because I could see such potential in the project. But looking in from the outside you can never get the whole picture.&lt;/p&gt;
&lt;p&gt;I went in with ideas about organising campaigns, streamlining delivery of content across multiple platforms, doing SWOT analyses, doing some video content ... but when I stepped aboard, I found out that this is a really focused company. They have one product, one target market, and they want to fulfil the requirements of that market well. Through experimenting they&apos;d already found out what works, and have a good idea of what is going to drive engagement, adoption and growth. Which is really reassuring for me: I don&apos;t have to start at &quot;why&quot;, nor do I need to define &quot;how&quot;, I only have to &quot;do&quot;.&lt;/p&gt;
&lt;p&gt;My first task was outlined pretty quickly: Build some starter kits with popular frontend frameworks. This would help me get used to using the Headless CMS part of Webiny (Webiny is much more than that, but we&apos;ll get to that later), give the company some more inbound links from authoritative domains, and help with user onboarding. It also would leverage my familiarity with these frameworks.&lt;/p&gt;
&lt;p&gt;I quickly found a bug: we have a custom rich text renderer package for React, which didn&apos;t compile to common javascript, which meant I couldn&apos;t use it in Next. This was a great opportunity to make my first contribution to Webiny!&lt;/p&gt;
&lt;p&gt;I struggled with figuring out how to customise our babel configuration, and Pavel, the CTO, helped me get through some silly misunderstanding I had about how to do that: how do you customise this babel configuration so that it overrides just this package, not any of the others in our monorepo?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = require(&quot;../../babel.react&quot;)({    path: __dirname, })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The answer is simply this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const defaults = require(&quot;../../.babel.react&quot;)({
    path: __dirname,
 })

 module.exports = {
     ...defaults,
     plugins: [
         ...defaults.plugins,
         &quot;@babel/plugin-transform-modules-commonjs&quot;,
     ]
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pavel and I laughed about how this got me, and thats pretty indicative of the whole team; they&apos;ve been welcoming, supportive and not critical of my mistakes. Of course, if I was a burden on the team I would expect there to be some further discussions, but you can anticipate a few teething problems from new team mates.&lt;/p&gt;
&lt;h2&gt;Contributing to open source again &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-two-weeks-at-webiny/#contributing-to-open-source-again&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Contributing to open source is just ... so satisfying. I built something I was pretty proud of at Purplebricks, but it&apos;s something I can&apos;t even show you, and the code is lost to me. I&apos;m pretty sad about that situation.&lt;/p&gt;
&lt;p&gt;On the other hand, I&apos;ve already made contributions to 2 Webiny repos, and have a draft PR open on NextJS docs. Because it&apos;s all public, it seems to me that this code is going to endure and be useful to other people. That is a great motivator for me.&lt;/p&gt;
&lt;h2&gt;Community interaction &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-two-weeks-at-webiny/#community-interaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The other thing I&apos;ve been excited about is getting more involved in the community via the Slack channel, it&apos;s been great to chat to other developers interested in Webiny, and it&apos;s helped me build my domain knowledge too.&lt;/p&gt;
&lt;p&gt;But much more than that, I&apos;ve been engaging on Discord servers, on Slack via &lt;a href=&quot;https://www.tnd.dev/&quot;&gt;The New Dynamic&lt;/a&gt;&apos;s community, as well as HackerNews, Reddit and in other places too.&lt;/p&gt;
&lt;p&gt;This has been great fun so far. I know I can&apos;t expect every interaction to be positive, but most are. This kind of community engagement scratches the itch I had before, when I wasn&apos;t just helping developers out with specific problems but seeing if I could raise a few eyes to the horizon, to see potential where they hadn&apos;t before, and to try new things.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/first-two-weeks-at-webiny/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First two weeks, as you see here, has been extremely positive. I&apos;m really excited to see where Webiny as a product goes. Also, I&apos;m blogging again ... which means things are looking up for my mental health.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:First 2 weeks at Webiny&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Freelancing: options and suggestions</title><link>https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/</guid><description>Many of us dream someday of freelancing and living a ... less restrictive? ... lifestyle. But is it truly the best option? I freelanced for 7 years before the Credit Crunch killed my business. Looking back, here are a few of the questions I&apos;ve been asked about freelancing. </description><pubDate>Mon, 07 Sep 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Many of us dream someday of freelancing and living a ... less restrictive? ... lifestyle. But is it truly the best option? I freelanced for 7 years before the Credit Crunch killed my business. Looking back, here are a few of the questions I&apos;ve been asked about freelancing.&lt;/p&gt;
&lt;h2&gt;Is it better to freelance? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#is-it-better-to-freelance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Is it better to freelance, or to work full time at one company? I think in a lot of ways, it is better to freelance. It does depend on your personality: are you risk averse, or do you mind living off your wits a little?&lt;/p&gt;
&lt;p&gt;I freelanced for a number of years, and it went OK. It provided about enough to support my wife and I until the 2008 recession hit … then it got bad really quickly. I used up all of our savings within the first year and had to find alternative employment. For myself, I don’t think I’d consider it now, but that’s mostly because I have 3 young kids to support. However, once they’ve grown up I’d really like to go back to it.&lt;/p&gt;
&lt;h2&gt;Do I need to be experienced? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#do-i-need-to-be-experienced&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I would say don’t worry about your experience level. The hardest thing to learn is how to manage business relationships, not how to code, and you can only learn both by practice … so I don’t think you need to worry too much, especially if you’re already building things so you have a portfolio (ie you can prove you can do what you&apos;re saying).&lt;/p&gt;
&lt;p&gt;When I started web development, it was a little bit by accident: I started a graphic design business, but one of my first clients wanted a website as well as branding. So I charged a small fee and learnt as I went, without having a clue how to do it.&lt;/p&gt;
&lt;p&gt;I didn&apos;t make any money, but I learned enough to get more web development work off the back of that, and building my own site.&lt;/p&gt;
&lt;h2&gt;Should I find a niche or generalise? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#should-i-find-a-niche-or-generalise&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are a lot of generalists in the freelance world: people who can work with multiple languages, turn their hand to many different things ... and there are those who specialise in a niche and get work from being specialists in the one subject.&lt;/p&gt;
&lt;p&gt;Finding a niche is quite valuable, but there are hazards too. If your niche dries up, then you’re going to have to pivot rather quickly onto another skill. But some niches can be very lucrative, for example I recently read that some government / industrial projects are still hiring FORTRAN developers. That blew my mind.&lt;/p&gt;
&lt;p&gt;If you maintain a generalist approach, you can turn your hand to most things. And that means you might be able to afford to be more selective in who you work with.&lt;/p&gt;
&lt;h2&gt;What tech stack should I use? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#what-tech-stack-should-i-use&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s no right answer to this one either, if I were to freelance and manage my own clients today, I’d probably do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a standard frontend that I can fork and iterate on quickly, probably leaning on an existing library to do the heavy lifting. I’d probably use Gatsby because I’m familiar with it and it has a great plugin ecosystem.&lt;/li&gt;
&lt;li&gt;Set up a multi-tenant backend so that I have only 1 codebase to maintain and keep my clients happy. I can’t decide how to do this ... it needs to be something that supports multiple tenants, so that might be a bit complex. I’d likely use Strapi or Webiny, or perhaps even a paid platform such as DatoCMS or Sanity to start with, until I’m confident in my own platform&lt;/li&gt;
&lt;li&gt;Set up monitoring and analytics tools on my own platforms using open source software so I can keep an eye on what’s going on without paying too much for it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is because I&apos;m a JavaScript developer and I love open source and indie web. You&apos;d likely make a different decision if you knew PHP or another language.&lt;/p&gt;
&lt;h2&gt;Contracting or freelancing? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#contracting-or-freelancing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I guess there’s broadly 2 types of freelancing open to developers:&lt;/p&gt;
&lt;p&gt;a) Building websites for clients directlyb) Contracting&lt;/p&gt;
&lt;p&gt;With contracting, you’re able to keep your hands on the tools, so to speak. But if you are building websites for clients, your primary concern is marketing, building your reputation, and keeping them happy. That involves certain sacrifices, though there’s more chance of “money on a sunday” from that one.&lt;/p&gt;
&lt;p&gt;What I mean by “money on a sunday” is that you can charge monthly fees for maintenance and hosting, and therefore you’ll be earning money when you’re not working (on Sundays!). That can become very lucrative. I know someone who makes 40% of their business income from those regular fees, and can afford to not take any more new clients on if they wanted to.&lt;/p&gt;
&lt;p&gt;So there’s no clear cut answer … and you could do both: have a few clients and contract in between. It could mean that sometimes you’re working all hours because you need to keep clients happy as well as keep your obligations to your contract firm.&lt;/p&gt;
&lt;h2&gt;Other tips &lt;a href=&quot;https://deliciousreverie.co.uk/posts/freelancing-options-suggestions/#other-tips&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Build your network quickly. Start as soon as possible. Use a small business network to get a foothold, connect with businesses on LinkedIn, canvass among businesses locally. Anything to get you connected.&lt;/li&gt;
&lt;li&gt;Get in touch with some good designers who are in need of developers. Quite often they are better at marketing and can pass you referrals if you do them favours in return.&lt;/li&gt;
&lt;li&gt;Advertise, advertise, advertise! Don&apos;t stop writing or producing content like podcasts, blog posts, vLogs ... be careful that doesn&apos;t take up too much of your time but you&apos;ll be more respected and likely get more work because of that.&lt;/li&gt;
&lt;li&gt;Build in a hosting and maintenance model for residual income. All technology needs to be maintained, if it isn&apos;t it&apos;ll break down sooner than it should. Sell that idea to your customers. If you can aim to get 20-40% of revenue this way, your life will be much easier.&lt;/li&gt;
&lt;li&gt;Build in a support model. Clients will often come to you for small updates or content updates (even if they have a cms!), because either they don&apos;t have the skill or the time to do it. Arrange a regular fee for those who come to you on this basis. And charge a high minimum 1-hour fee for those who don&apos;t (to push them towards the regular maintenance).&lt;/li&gt;
&lt;li&gt;Hire an accountant as soon as you can afford to do so. They can keep on top of things like taxes, late invoices and they&apos;ll find out where you can claim or save money.&quot;&lt;/li&gt;
&lt;/ol&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Freelancing: options and suggestions&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title> From Wordpress to Developing in React — Starting to See It</title><link>https://deliciousreverie.co.uk/posts/from-wordpress-to-developing-in-react-starting-to-see-it/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/from-wordpress-to-developing-in-react-starting-to-see-it/</guid><description>I recently decided to dive into learning React with a few courses and some experimentation. I had the aim of building a site in Gatsby.js as a test for building sites entirely in React. This is what I found out. This article was published on the gatsby.js blog. </description><pubDate>Thu, 12 Oct 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;As a frontend designer I&apos;ve always prided myself on being an HTML and CSS specialist. My use of JavaScript has been limited to animations and DOM manipulation. However, at the agency I work we&apos;re branching out from our staple of WordPress and trying different methods of creating websites with functionality that our clients require, whilst maximizing their budget.&lt;/p&gt;
&lt;p&gt;We&apos;re also bracing ourselves for what we’re anticipating to be the stormfront of &lt;a href=&quot;https://wordpress.org/plugins/gutenberg/&quot;&gt;Gutenberg&lt;/a&gt;for our WordPress projects. Once it&apos;s included in the WordPress Core, writing components in JavaScript for the Gutenberg editor will be necessary for every developer on a project.&lt;/p&gt;
&lt;p&gt;So I decided to dive into learning React with a few courses and some experimentation. I had the aim of building a site in &lt;a href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gatsby.js&lt;/a&gt; as a test for doing projects built entirely in React.&lt;/p&gt;
&lt;h2&gt;Letting Go&lt;/h2&gt;
&lt;p&gt;The first thing I had to do while investigating React was to let go of some preconceived ideas I had – that JSX is just plain weird, that JSS (JavaScript Stylesheets) was an unnecessary abstraction. So I tried not to be bothered by the templating weirdness too much and embrace the differences. My &lt;code&gt;class&lt;/code&gt;es soon became &lt;code&gt;classNames&lt;/code&gt;s, my &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags became &lt;code&gt;&amp;lt;Links&amp;gt;&lt;/code&gt;, and I quickly saw what people had been enthusing about.&lt;/p&gt;
&lt;p&gt;Managing everything in one place is such a cathartic experience.&lt;/p&gt;
&lt;p&gt;With WordPress templating, I used to have function files, filter files, template files, and Sass partials, often with similar names, depending on the framework I&apos;d be using. To build a site meant needing to buffer the entire project in my short-term memory. Not an easy feat to manage.&lt;/p&gt;
&lt;p&gt;And then you would complete a project, and forget it all entirely. Until it went to the client…&lt;/p&gt;
&lt;p&gt;Every time a client returned with a last-minute change, I would open up the code (which I might not have touched for weeks or months), and try to grok what function lived where, or what I had called that Sass partial. Even with the help of browser dev tools, this can be really annoying.&lt;/p&gt;
&lt;p&gt;Now with Gatsby and React, I have my logic in one language, and in a way that makes groking easier. I have my layout, template and config modules at hand in the folder structure, without duplication.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;
// Template for blog page
export default function BlogPost({ data }) {
  const post = data.markdownRemark
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{post.data.title}&amp;lt;/h1&amp;gt;
      &amp;lt;div dangerouslySetInnerHTML={{ __html: post.html }} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
// The data query
export const query = graphql`
  query BlogPostQuery($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        type
      }
    }
  }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whilst this might look weird, it actually makes it much easier to understand what’s going on. You know you’re dealing with this data, using this HTML, and with CSS-in-JS strategies such as Styled Components, you can see exactly what CSS is going to be implemented too. In one file.&lt;/p&gt;
&lt;p&gt;Beautiful.&lt;/p&gt;
&lt;h2&gt;GraphQL: SQL-like data queries&lt;/h2&gt;
&lt;p&gt;One thing I particularly love about Gatsby.js is its inclusion of &lt;a href=&quot;https://graphql.org/&quot;&gt;GraphQL&lt;/a&gt;. Like SQL, you use GraphQL to query your data, whether from the WordPress API, Contentful or Markdown, and extract a dataset to display in your template.&lt;/p&gt;
&lt;p&gt;This approach to data is really adaptable. I love the fact that you can install a plugin and query your API endpoints with such ease.&lt;/p&gt;
&lt;p&gt;Gatsby.js comes with GraphiQL, which is a simple web-based IDE so you can query your data and get back examples of content immediately. You can then copy this query into your React module and get back the information you need, whether it&apos;s the title, content, featured image, categories or any other frontmatter you might have set up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export const query = graphql`
  query BlogPostQuery($slug: String!) {
    markdownRemark(fields: { slug: { eq: $slug } }) {
      html
      frontmatter {
        title
        type
      }
    }
  }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aside from those pesky tick characters, which are sometimes hard to spot for a newbie, I think this is a great tool, and has sped up my development a significant amount.&lt;/p&gt;
&lt;h2&gt;CSS in JS&lt;/h2&gt;
&lt;p&gt;This is the thing that I found hardest about React. I&apos;ve tried 3 methods so far, and don&apos;t really love any of them.&lt;/p&gt;
&lt;p&gt;However, since I&apos;m managing my HTML with JavaScript, why not CSS as well? As above, having everything in one place simplifies the workflow and allows you to focus on context without having to grok SCSS again, reducing mental friction.&lt;/p&gt;
&lt;h2&gt;My Project&lt;/h2&gt;
&lt;p&gt;Following the &lt;a href=&quot;https://www.gatsbyjs.com/tutorial/&quot;&gt;tutorial on Gatsbyjs&lt;/a&gt; I built up my project from scratch, breaking things profusely at first, but it honestly didn’t take long to gain confidence enough so that I launched my first site at &lt;a href=&quot;https://freebabylon5.com/&quot;&gt;https://freebabylon5.com&lt;/a&gt; recently.&lt;/p&gt;
&lt;p&gt;Be warned: the tutorial isn’t quite finished yet, you might be better off starting with &lt;a href=&quot;https://www.gatsbyjs.com/starters/&quot;&gt;one of the starter kits already available&lt;/a&gt;, so that you get react-helmet and active links implemented, the 2 things I had to learn independently.&lt;/p&gt;
&lt;h2&gt;The Way Forward?&lt;/h2&gt;
&lt;p&gt;My initial concerns around using a JavaScript framework such as React seem to have all been allayed. With server-side React, we no longer have a dependency on frontend JavaScript, so progressive enhancement is not just a possibility but a standard. There&apos;s momentum towards better accessibility, and for me as a developer, the tools are there (such as Babel, Chrome Dev Tools React extension, and others) for a faster, more efficient development experience.&lt;/p&gt;
&lt;p&gt;I&apos;m glad to say I&apos;m sold on the idea and methods of developing with JavaScript, and React in particular. The site I&apos;ve re-built from WordPress into Gatsby.js is now live at &lt;a href=&quot;https://freebabylon5.com/&quot;&gt;https://freebabylon5.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Together with my colleagues at &lt;a href=&quot;https://indigotree.co.uk/&quot;&gt;Indigo Tree&lt;/a&gt; we’re now looking at using WordPress as a backend, where clients can edit their content without the worries of insecure plugins or other methods of being hacked.&lt;/p&gt;
&lt;p&gt;Using GatsbyJS with its “Bring Your Own Data” strategy makes perfect sense, and we’re about to start building our first Gatsby client site using the plugin &lt;a href=&quot;https://www.gatsbyjs.com/packages/gatsby-source-wordpress/&quot;&gt;gatsby-source-wordpress&lt;/a&gt; to pull in our data and build a totally secure website with some pretty impressive gains on loading time. We’ll also sleep better at night knowing insecurities in WordPress are no longer putting our clients at risk.&lt;/p&gt;
&lt;p&gt;The web is always changing. And the way forward isn&apos;t always easy to see. Now, with GatsbyJS, we&apos;re beginning to feel like we can visualize where things are going.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re: From Wordpress to Developing in React — Starting to See It&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Fun with Callbacks</title><link>https://deliciousreverie.co.uk/posts/fun-with-callbacks/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/fun-with-callbacks/</guid><description>I had the opportunity to integrate a callback function with a module I was building recently. But what is a callback? How do you use them? Let&apos;s take a closer look.</description><pubDate>Sun, 26 Feb 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I had the opportunity to integrate a callback function with a module I was building recently. But what is a callback? How do you use them? Let&apos;s take a closer look.&lt;/p&gt;
&lt;p&gt;I was building a module that could be pulled into a PHP application to upload videos using Uppy. But once the video was uploaded, there was another step that needed to run: the uploaded file needed to be sent to another application to be processed.&lt;/p&gt;
&lt;p&gt;The original API I envisaged was something like this. A script could be placed on the page (wherever the backend team wanted the uploader to appear). They could pass a DOM element and a message to be displayed on the upload screen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      largeFileUploader(
        &quot;.large-file-uploader&quot;, // target DOM element
        &quot;message&quot;, // Message to be displayed
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The application itself was rendered with Uppy, and included some form validation and other things to help the users upload their videos easily:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function largeFileUploader(
  targetDomElement,
  onScreenMessage,
  callback
) {
  const uppy = new Uppy({
    .use(Dashboard, {
      target: targetDomElement,
      inline: true,
      note: onScreenMessage,
      height: 470,
      theme: &quot;auto&quot;,
      showProgressDetails: true,
    })
    // ... other configuration
   Uppy.on(&quot;complete&quot;, () =&amp;gt; {
   // do something when Uppy has uploaded all files
   };
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To provide functionality for the callback, I passed an extra parameter to my function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      largeFileUploader(
        // target DOM element
        &quot;.large-file-uploader&quot;,
        // Message to be displayed
        &quot;message&quot;,
        () =&amp;gt; {
          // this code runs inside the context of the uploader
        }
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this extra parameter, you can run the function inside the context of your application and do something with the result, for example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      largeFileUploader(
        &quot;.large-file-uploader&quot;, 
        &quot;message&quot;,
        async (formData) =&amp;gt; {
          try {
            const getStuff = await fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;, {
            method: &quot;GET&quot;,
            headers: {
              &quot;Content-Type&quot;: &quot;application/json&quot;,
            },
          })
          const getStuffJson = await getStuff.json()
          return { success: &apos;message&apos; }
          } catch (error) {
            return error?.message
          }
        });
    &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows us to provide an extra notification, rendered by Uppy,  to inform users that the video has been sent for processing, even though this step has happened outside of the context of Uppy. As my friend Chris Geary said, &quot;The callback you pass is just a reference to a function, so in theory, as long as that reference remains in scope, you can call it.&quot;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function largeFileUploader() {
   ... // other code
   Uppy.on(&quot;complete&quot;, async (result) =&amp;gt; {
      const data = await callback()
      if (data?.success) {
        uppy.info(`✅ Video processing initiated`, &quot;info&quot;, 15000);
      }
      if (data?.error) {
        uppy.info(
          `⛔️ Upload to processor failed, please contact support.`,
          &quot;error&quot;,
          15000
        );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Callbacks are super useful for rendering extra functionality provided by the people using your application code.&lt;/p&gt;
&lt;p&gt;Further reading:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Callback_function&quot;&gt;https://developer.mozilla.org/en-US/docs/Glossary/Callback_function&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://callbackhell.com&quot;&gt;http://callbackhell.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#application-security&quot;&gt;https://cheatsheetseries.owasp.org/cheatsheets/Nodejs_Security_Cheat_Sheet.html#application-security&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Fun with Callbacks&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Predictions for GatsbyConf 2021</title><link>https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/</guid><description>It&apos;s no secret that NextJS is knocked the socks off Gatsby last year in terms of developer adoption. I think the frameworks will keep learning from each other (read: steal features), and it&apos;s a good thing for both frameworks. Here&apos;s my take on what could be announced at Gatsby&apos;s forthcoming conference. </description><pubDate>Fri, 19 Feb 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;It&apos;s no secret that NextJS is knocked the socks off Gatsby last year in terms of developer adoption. I think the frameworks will keep learning from each other (read: steal features), and it&apos;s a good thing for both frameworks.&lt;/p&gt;
&lt;p&gt;Dustin Schau &lt;a href=&quot;https://twitter.com/schaudustin/status/1362223485523648512?s=21&quot;&gt;asked in this tweet&lt;/a&gt;, &quot;What do you think we&apos;re launching at #GatsbyConf on March 2nd?&quot;&lt;/p&gt;
&lt;p&gt;Here&apos;s my take on what could be announced at Gatsby&apos;s forthcoming conference.&lt;/p&gt;
&lt;h2&gt;1. Hosting on Gatsby Cloud &lt;a href=&quot;https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/#1.-hosting-on-gatsby-cloud&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using Gatsby Cloud as part of a preview environment has been a great innovation, especially the work that has gone into the speed and ease of incremental builds.&lt;/p&gt;
&lt;p&gt;Building out this existing functionality into a fully-fledged hosting platform to compete with the likes of Vercel and Netlify makes sense, especially if deploys benefit from that same speed.&lt;/p&gt;
&lt;h2&gt;2. Server functions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/#2.-server-functions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the reasons many developers love NextJs is that it isn&apos;t restricted to being exported as a static site. Vercel, Netlify and AWS Amplify and some other platforms allow you to run serverless functions which complement the application, so that you don&apos;t have to render all of your content on build, instead when a page is requested for the first time, it can be generated then.&lt;/p&gt;
&lt;p&gt;Allowing this isomorphic approach is definitely on Gatsby&apos;s radar, and it would really be good to see it a first class citizen on &lt;a href=&quot;https://www.gatsbyjs.com/cloud/&quot;&gt;Gatsby Cloud&lt;/a&gt;, their hosting platform that will compete with Vercel, Netlify, and all the other offerings that have sprung up in the last year or two.&lt;/p&gt;
&lt;p&gt;This will probably mean those platforms will have to do some work to ensure they are compatible with this new way of using Gatsby, so expect a bit of a delay before they are able to use this new version of Gatsby.&lt;/p&gt;
&lt;h2&gt;3. GraphQL Toolkit &lt;a href=&quot;https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/#3.-graphql-toolkit&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of my biggest issues with Gatsby at the moment is that data from GraphQL APIs isn&apos;t able to be transformed. If you have a CMS that supplies rich text or Markdown, you have to write that transformation in the template, so it gets packaged into your bundle and transformed in the JavaScript runtime.&lt;/p&gt;
&lt;p&gt;It&apos;s a huge problem, but the &lt;a href=&quot;https://github.com/gatsbyjs/gatsby-graphql-toolkit&quot;&gt;GraphQL Toolkit project&lt;/a&gt; looks really promising. Here&apos;s hoping it&apos;s ready to be merged in!&lt;/p&gt;
&lt;h2&gt;4. Some new LowCode / NoCode features &lt;a href=&quot;https://deliciousreverie.co.uk/posts/gatsbyjs-predictions-2021/#4.-some-new-lowcode-nocode-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Gatsby is aiming for a very different market to NextJS ... it&apos;s chief market are newer or less experienced developers who are building marketing sites for smaller organisations. WordPress proved that there&apos;s a huge volume of these and plenty of scope to facilitate a world where developers can pick a theme, add content, make some customisations, set up hosting and charge a client a fee for doing so.&lt;/p&gt;
&lt;p&gt;To what extent Gatsby wants to enter this market is anybody&apos;s guess. I&apos;m hoping they don&apos;t get too invested into it; I think it&apos;s healthy for Gatsby to still take on the NextJS market (to my mind this is more experienced developers who want a React &amp;amp; Node framework for their applications). However there&apos;s good money to be had and its doing a much better job than the current incumbent in this space.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Predictions for GatsbyConf 2021&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Getting Started with TypeScript</title><link>https://deliciousreverie.co.uk/posts/getting-started-with-typescript/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/getting-started-with-typescript/</guid><description>TypeScript. You either love it, or hate it ... or are terrified of it. But it could be a valuable tool that benefits your workflow. This article, originally published in Net magazine, shows you how to get started with TypeScript </description><pubDate>Thu, 22 Aug 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Mention TypeScript to a developer who works with JavaScript and you&apos;ll probably get one of two responses: they will either struggle to understand why someone would NOT want to use TypeScript, or they will struggle to see why someone WOULD use it.&lt;/p&gt;
&lt;p&gt;I think this sometimes depends on where you started your journey to JavaScript. Often people come to the language from a server-side language such as C or Java, which are statically typed. In these languages, dynamic typing is seen as a real hazard, an unsafe practice.&lt;/p&gt;
&lt;p&gt;But if you&apos;ve started your journey as a developer from HTML and CSS, you&apos;ve probably been more used to the generally more ... dynamic ... world of the frontend.&lt;/p&gt;
&lt;p&gt;If this is the case, but you hate the fact that your application is blowing up in production, and the cause is often a typeError, or you want to improve communication between developers working on a project, TypeScript can be a good solution.&lt;/p&gt;
&lt;p&gt;In this article we&apos;re going to introduce some of the concepts of TypeScript using Styled Components. I&apos;ve chosen this route because I think it gives us a good insight into how certain features can be used without introducing more complicated concepts too soon.&lt;/p&gt;
&lt;h2&gt;Let&apos;s Get Started! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#let&apos;s-get-started!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this tutorial, we&apos;re going to take a react template that uses styled components and move it to TypeScript.&lt;/p&gt;
&lt;p&gt;The original code we&apos;re going to use is here: &lt;a href=&quot;https://codesandbox.io/s/netmag-react-typescript-4l4cj&quot;&gt;https://codesandbox.io/s/netmag-react-typescript-4l4cj&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Fork this code, and open on the Container.js module. We&apos;ll work through it together to convert it to TypeScript.&lt;/p&gt;
&lt;p&gt;The project on CodeSandbox already has a compiler set up for TypeScript. So we can start by renaming our file. Instead of .js at the end, change it to .tsx.&lt;/p&gt;
&lt;p&gt;Don&apos;t forget the x! This tells the compiler to use JSX, the templating language often used with React. If you didn&apos;t have an x at the end, you would start to see some very verbose errors very quickly.&lt;/p&gt;
&lt;p&gt;Already, TypeScript is working for us. By forcing us to have an x at the end of the file, we can see that this module contains a JSX template. So it&apos;s easier to see at a glance what&apos;s going on in each file.&lt;/p&gt;
&lt;h2&gt;What is an Interface? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#what-is-an-interface&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript works by forcing us to tell it what properties are going into, and coming out of, our functions. Since styled components are functions, we have to type these as well.&lt;/p&gt;
&lt;p&gt;Let&apos;s add our first interface!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface ISContainerProps {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we&apos;re declaring what will interface with our function. As a convention we should use a capital I at the start of the name so that we can more easily see later that it is an interface, instead of anything else (more about what else it could be in a bit ...)&lt;/p&gt;
&lt;p&gt;Let&apos;s add our first property:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface ISContainerProps {  bgColor: string;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ve given bgColor the type &quot;string&quot; here. This means that it can only be a string value. If someone comes to use our component and writes bgColor={42}, they&apos;ll see a red underline, indicating an invalid type has been used! Usually this means a PR won&apos;t pass quality checks or you will be prevented from pushing code to a branch in the repo.&lt;/p&gt;
&lt;p&gt;I want to restrict the use of the next property, text colour, a little more than that. I want them to have the choice between only black and gray text. Because of this, I&apos;m going to rename textColour to lighterText:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    lighterText?: boolean;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The question mark (?) tells the compiler that lighterText is an optional property. If nothing is declared, it&apos;ll be false by defaut. Within my styled component, I can output grey or black based on this boolean value:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    color: ${props.lighterText =&amp;gt; (props.lighterText ? `black` : `gray`)};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This can be shortened a little bit by some destructuring:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    color: ${({ lighterText }) =&amp;gt; (lighterText ? `black` : `gray`)};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Introducing Enums: A Type of Types &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#introducing-enums:-a-type-of-types&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For my final property, I want to introduce you to the humble enum. enums, I&apos;m told by reliable sources (wikipedia), have been around for a while. Instead of a normal type, say a boolean, string or number, an enum is it&apos;s own type.&lt;/p&gt;
&lt;p&gt;My enum is going to declare the container widths I want people to have access to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export enum EContainerWidth {
  SMALL = &quot;36rem&quot;,
  MEDIUM = &quot;48rem&quot;,
  LARGE = &quot;64rem&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can you see the name I&apos;ve given the enum starts with a capital E? This is the equivalent of using a capital I for the interface.&lt;/p&gt;
&lt;p&gt;Now I&apos;m going to declare that my containerWidth property is a type of EContainerWidth:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface ISContainerProps {
  bgColor: string;
  lighterText: boolean;
  containerWidth: EContainerWidth;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes sure someone can only use these values for the container width. In this way, how a developer uses this module can be controlled more tightly. So, no more accidentally setting the container width to something that doesn&apos;t match the design. It also helps a developer to stop and think more carefully about their choices.&lt;/p&gt;
&lt;p&gt;So, now, how do I tell my styled component about my interface? By adding chevrons after the tag declaration, but before the body of the function (inside the backticks), and writing the name of my interface there:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface ISContainerProps {
  containerWidth: EContainerWidth;
}

const SContainer =
  styled.div &amp;lt;
  ISContainerProps &amp;gt;
  `
    max-width: ${({ containerWidth }) =&amp;gt; containerWidth}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Typing the Template &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#typing-the-template&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now we need to tell the compiler what the type is for our JSX template. I&apos;m using a React functional component (FC) for this project. The type for this can be imported directly from React at the top of our module:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { FC } from &quot;react&quot;;

export const Container: FC = ({
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we need to pass in our properties. They&apos;re the same as the ones we&apos;ve used above, with one addition: the children, other react nodes we want to render inside the template.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface IContainerProps {}

export const Container: FC&amp;lt;IContainerProps&amp;gt; = ({
    ...
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interfaces can be re-used and extended by other interfaces. This is going to be particularly useful when it comes to our JSX template:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export interface IContainerProps extends ISContainerProps {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the template has the same types as our styled component. Now let&apos;s add the additional property. We mentioned that this was a React node, and that&apos;s another type we can import from react:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import {FC, ReactNode} from &apos;react&apos;

...

export interface IContainerProps extends ISContainerProps {
    children: ReactNode;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are ready to use our template!&lt;/p&gt;
&lt;h2&gt;Using the Template &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#using-the-template&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&apos;ve declared an enum for our container width. We need other developers to have access to this when using our template.&lt;/p&gt;
&lt;p&gt;Because we have already exported our enum, we can import it along with our component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Container, { EContainerWidth } from &quot;./Container&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have a global file in my src/ folder (usually I call it &quot;tokens&quot;) where all of my project&apos;s enums are kept. This is much better for helping others discover what properties they have access to, and it&apos;s much easier to re-use them.&lt;/p&gt;
&lt;p&gt;Now, instead of assigning properties to the template at will, every time you or another developer comes to use it, you or they will have reason to pause and make sure they&apos;re using it in the way it was intended:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Container
  bgColor=&quot;red&quot;
  lighterText={true}
  containerWidth={EContainerWidth.MEDIUM}
&amp;gt;
  JavaScript Container
&amp;lt;/Container&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you got this far and followed along, congratulations on making your first TypeScript component!&lt;/p&gt;
&lt;h2&gt;Start Using TypeScript today &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#start-using-typescript-today&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript is one of the fastest growing trends in JavaScript. Reinforcing types in your application can help you avoid making fatal errors in production, especially when working with teams of developers. By using TypeScript we can lean on our IDEs more and simplify our workflow, introduce self-documentation and better code hinting.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: What is TypeScript? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#boxout:-what-is-typescript&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript was developed by Google specifically for the frontend framework AngularJS. It&apos;s not a separate language to JavaScript, but a superset, it adds to the existing language. You can write in normal JavaScript, but you add special syntax to identify types for functions, variables etc.&lt;/p&gt;
&lt;p&gt;TypeScript needs to be compiled to normal JavaScript to be used on a browser. But this is an advantage: errors that are generated can be caught and mitigated before anything goes into production.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Static vs Dynamic Typing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#boxout:-static-vs-dynamic-typing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the most powerful features of any programming language is the ability to store things in allocated memory, often called a variable, the value of which can be updated or changed by the program.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// define a variable
var a = 1;

console.log(a); // 1

// define a function which will change it&apos;s value
function updateAVariable() {
  a = &quot;a is now a string&quot;;
}

// call the function
updateAVariable();

console.log(a); // &quot;a is now a string&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This demonstrates how powerful JavaScript&apos;s dynamic typing is. The variable a can be anything: a string of letters, a number, whatever you want.&lt;/p&gt;
&lt;p&gt;The problem with that is it can easily break stuff:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function addOneToA() {
  a = a + 1;
}
addOneToA();

console.log(a);

// &apos;a is now a string1&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By contrast, static typing means that you&apos;ll only use one type of thing, and if that changes, you will know about it before you even run the code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a: number = 1;

function updateAVariable() {
  a = &quot;a is now a string&quot;;
  // typeError!
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Imagine our variable comes from another location, which is written or controlled by another team. Imagine that variable, somehow and without your knowledge, starts returning a string instead of a number.&lt;/p&gt;
&lt;p&gt;In that case, we now have a failure in the application. It could be crashing in production. The only way to check it might be to manually go through the checkout process, add something to your cart and try to purchase an item.&lt;/p&gt;
&lt;p&gt;Instead, your code editor, terminal or CI tool lets you know, potentially before even running the code that there could be the possibility of a failure like this in your application.&lt;/p&gt;
&lt;p&gt;That&apos;s one reason why I like using Typescript: my code editor tells me where I&apos;ve made a mistake, and it won&apos;t let me push code to my Git repo unless some TypeScript rules I&apos;ve given it are followed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Resources &lt;a href=&quot;https://deliciousreverie.co.uk/posts/getting-started-with-typescript/#boxout:-resources&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are some great resources for TypeScript out there, and they&apos;re growing as more people write about their experience with the language. Here are some places to start:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://www.typescriptlang.org/&lt;/li&gt;
&lt;li&gt;https://definitelytyped.org/guides/best-practices.html&lt;/li&gt;
&lt;li&gt;https://github.com/dzharii/awesome-typescript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main problem I have with TypeScript is that people writing about it assume you&apos;ve got good conceptual programming understanding to begin with, so it&apos;s hard to pick up for newer developers.&lt;/p&gt;
&lt;p&gt;If you want a good beginner level course on TypeScript, I can really recommend this one by Scott Tolinski. Scott is great at explaining things simply and clearly without assuming too much:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.leveluptutorials.com/tutorials/level-1-typescript&quot;&gt;https://www.leveluptutorials.com/tutorials/level-1-typescript&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Getting Started with TypeScript&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Hidden Histories</title><link>https://deliciousreverie.co.uk/posts/hidden-histories/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/hidden-histories/</guid><description>I&apos;ve spent the last 2 years living and working around Welsh language speakers. I still find it amazing that a whole culture existed here without my ever encountering it before.</description><pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I feel I need to start this article with a brief explanation: Welsh is one of the languages spoken in the British Isles.&lt;/p&gt;
&lt;p&gt;It seems incredible to me that I need to explain this, but in my experience a lot of people I&apos;ve personally encountered don&apos;t seem to be aware of its existence.&lt;/p&gt;
&lt;p&gt;The Welsh language is not Gaelic. It&apos;s distantly related to the language of Ireland, but they went their separate ways many centuries ago.&lt;/p&gt;
&lt;p&gt;It&apos;s more accurately referred to as Brythonic. In fact, if you go back far enough on maps or books, you&apos;ll find it referred to as &quot;The British language&quot;.&lt;/p&gt;
&lt;p&gt;And there begins our journey of discovery.&lt;/p&gt;
&lt;h2&gt;An Awakening&lt;/h2&gt;
&lt;p&gt;I heard recently of a musician who had been dead set against learning Welsh who, when he finally picked it up, has done so enthusiastically: as a musician he appreciates the beautiful lyrical quality of the language, how it naturally lends itself to song, poetry and music.&lt;/p&gt;
&lt;p&gt;His experience mirrors my own: I hated learning Welsh when I was young. But now, for me, it has unlocked my own history, one which I was completely unaware of; it&apos;s made my experience of living here that much richer and significant.&lt;/p&gt;
&lt;p&gt;I think I can only appreciate this as an adult; now I&apos;m older I am more aware of and appreciate more the efforts of my parents, and by extension, my extended family, in shaping who I am.&lt;/p&gt;
&lt;h2&gt;A Hidden Heritage&lt;/h2&gt;
&lt;p&gt;Wales has shaped the identity of many people in the British Isles, but many don&apos;t even realise it.&lt;/p&gt;
&lt;p&gt;For example, the Welsh are often mocked for having few surnames. In one place there are a whole village of people with the surname &quot;Morris&quot;. &quot;Jones&quot; and &quot;Davies&quot; are very common too.&lt;/p&gt;
&lt;p&gt;What people fail to realise is that many other &quot;English&quot; surnames have a Welsh heritage too.&lt;/p&gt;
&lt;p&gt;&quot;Powell&quot; and &quot;Bevan&quot; include a leftover from the Welsh prefix &quot;ap&quot;, which means &quot;son of&quot;. What was once &quot;ap Howell&quot; is now &quot;Powell&quot;. &quot;ap Evan&quot; simlarly became &quot;Bevan&quot;.&lt;/p&gt;
&lt;p&gt;Here&apos;s another example, and it&apos;s probably an even more interesting one: the origins of place names. Wherever you see &quot;tre&quot;, &quot;pont&quot; or &quot;caer&quot;, you&apos;re looking at a Welsh place name.&lt;/p&gt;
&lt;p&gt;There are a number of rivers in Britain called &quot;Avon&quot;, the Welsh word for &quot;river&quot;.&lt;/p&gt;
&lt;p&gt;This is because Welsh isn&apos;t ... Welsh. The whole of the British mainland once spoke Welsh, before the invasion of first the Romans, then the Anglo Saxon tribes ... which is where we now get the name &quot;England&quot;.&lt;/p&gt;
&lt;p&gt;Interestingly the Welsh refer to England as &quot;Lloeger&quot;, the &quot;lost lands&quot;.&lt;/p&gt;
&lt;p&gt;For these reasons, I strongly suspect there are many more people in England who will be able to trace at least part of their heritage to a Welsh speaking person as well as Angles, Saxons, Romans and Normans.&lt;/p&gt;
&lt;p&gt;I find it unfortunate that the heritage of these other peoples  prevail in our culture, whereas Welsh culture is sidelined and sometimes ridiculed.&lt;/p&gt;
&lt;h2&gt;Ridiculed then valued&lt;/h2&gt;
&lt;p&gt;The territory of Wales has been reduced even more in recent decades, with more English speaking people moving in who don&apos;t see the need to learn the language. &quot;They all speak English anyway&quot; is a common reason for monolingualism.&lt;/p&gt;
&lt;p&gt;But so do Native American peoples, Maori and indigenous Australians. I feel it&apos;s a shame to miss out on this opportunity to get to know the people and land around you. Also, there are clearly researched academic and life skills benefits to raising children in bilingual households.&lt;/p&gt;
&lt;p&gt;There&apos;s a lot of water under the bridge between English speaking and Welsh speaking people, and I&apos;m loath to speak about it in any detail. Suffice to say the wounds are still there, scarred over and sometimes mentioned in passing ... and only among Welsh speakers and in academic circles.&lt;/p&gt;
&lt;p&gt;But given that history I find myself wanting to defend the language.&lt;/p&gt;
&lt;p&gt;&quot;Welsh is a foreign language&quot; is one I heard recently. I find this perhaps more uncomfortable than most, since the meaning of the word &quot;Welsh&quot; is &quot;foreigner&quot;. Essentially, Welsh speakers are foreigners even in their own homes!&lt;/p&gt;
&lt;p&gt;But the times are changing: I recently spoke to a mother from  an almost entirely English speaking part of Wales who wanted to learn Welsh. Why? &quot;My grandkids speak Welsh to each other, and I want to be able to speak to them in the language of their hearts.&quot;&lt;/p&gt;
&lt;p&gt;I find this fascinating; the demographic of Welsh speakers is changing: in the older generation its more common to find speakers, then there&apos;s a dip where Welsh as a language was actively oppressed, and now it&apos;s being encouraged again so an increasing number of young people speak it.&lt;/p&gt;
&lt;p&gt;I wonder what this will do to the language?&lt;/p&gt;
&lt;p&gt;At any rate, in a world where there&apos;s a lot of movement of families and individuals, I&apos;m sure it will continue to change.&lt;/p&gt;
&lt;p&gt;And I&apos;m ok with that, as long as it doesn&apos;t disappear.&lt;/p&gt;
&lt;h2&gt;Welsh music&lt;/h2&gt;
&lt;p&gt;If you enjoyed reading about this cultural sidequest of mine, maybe you&apos;d be interested to hear about Welsh music. As I mentioned earlier, the language expressed in song is often mesmerisingly beautiful.&lt;/p&gt;
&lt;p&gt;Look up &quot;Mari Matthias&quot;, &quot;Bwncath&quot; and &quot;Adwaith&quot; on any music streaming service to see what I mean.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe data-testid=&quot;embed-iframe&quot; style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/track/2XhSoYS2gVFh9oqJnV0F18?utm_source=generator&quot; width=&quot;100%&quot; height=&quot;152&quot; frameBorder=&quot;0&quot; allowfullscreen=&quot;&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot; loading=&quot;lazy&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe data-testid=&quot;embed-iframe&quot; style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/track/5M5mX2dt8zUpczGLoea29E?utm_source=generator&quot; width=&quot;100%&quot; height=&quot;152&quot; frameBorder=&quot;0&quot; allowfullscreen=&quot;&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot; loading=&quot;lazy&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe data-testid=&quot;embed-iframe&quot; style=&quot;border-radius:12px&quot; src=&quot;https://open.spotify.com/embed/track/1F13FMD7OJRtvldPkF9hs8?utm_source=generator&quot; width=&quot;100%&quot; height=&quot;152&quot; frameBorder=&quot;0&quot; allowfullscreen=&quot;&quot; allow=&quot;autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture&quot; loading=&quot;lazy&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Hidden Histories&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How I Created My Home Web Server</title><link>https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/</guid><description>I decided to try to build my own web server using my home router, an old PC, and a Linux operating system.</description><pubDate>Sat, 18 Feb 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Is it just me or does Apple&apos;s iCloud seem like a very simple solution to a problem all of us in a connected world face? That of storing, managing and sharing photos of our loved ones, holidays and interests with others?&lt;/p&gt;
&lt;p&gt;Instead of being faced with ever increasing charges from Apple to store my stuff, I decided to get a solution I could be more in control of.&lt;/p&gt;
&lt;h2&gt;You Will Need &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#you-will-need&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I could have done this project by setting it up on my own existing home computer, but I already had a spare laptop that had been given to me. I also thought it would give me the freedom to wipe everything and start again if I messed it up irreversibly (which, coincidentally, did end up happening!).&lt;/p&gt;
&lt;p&gt;I decided that I&apos;d like to use the current LTS issue of Debian for my hosting platform. I liked the idea of using a bare bones OS, something that I hoped I would be able to customise and learn with. I installed this OS and got to know it, especially how to update and install core and third party packages.&lt;/p&gt;
&lt;p&gt;The main commands for this are:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;First hitch: Packages not upgrading &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#first-hitch:-packages-not-upgrading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;About the first thing I noticed when I went into the Terminal app from the GUI was this message that I could upgrade packages, but when I tried to install stuff, it said that no packages were updated.&lt;/p&gt;
&lt;p&gt;After some research, I found out it was because I needed to extend the Sources list to include some other sources, &lt;a href=&quot;https://wiki.debian.org/SourcesList&quot;&gt;as explained on the wiki&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was then able to install the AMP stack (Apache, Mysql, Php) and it gave me immense pleasure to see a &quot;hello world&quot; message when I added a php file to the www directory.&lt;/p&gt;
&lt;h4&gt;Second hitch: Permissions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#second-hitch:-permissions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;At this point, I found that I was having trouble getting the right permissions for PHP and MySQL to operate.&lt;/p&gt;
&lt;p&gt;I didn&apos;t know at the time that the solution is to create a new group (its usually &quot;www&quot;), add the current user to the group, then add the user used by Apache (www-data) to this group.&lt;/p&gt;
&lt;h4&gt;Third hitch: Sharing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#third-hitch:-sharing&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;At this point, I got stuck trying to see how to see my website from another computer in my local network. It seemed that whatever IP I went to, nothing worked.&lt;/p&gt;
&lt;p&gt;I went down a bit of a maze, installing Samba file sharing system before second-guessing myself into the belief that I had taken a serious wrong turn somewhere previously. And now I had Samba, NFS and other file sharing services turned on, potentially exposing my computer to weaknesses.&lt;/p&gt;
&lt;p&gt;So in order to progress quickly and avoid having to retrace all my steps, I wiped the computer.&lt;/p&gt;
&lt;p&gt;It might seem a little drastic, that, but it did get me out of that situation quickly!!&lt;/p&gt;
&lt;h2&gt;Second Attempt &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#second-attempt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even though there were a lot of reasons to like Debian I decided that the community support just wasn&apos;t there for me. It&apos;s not a criticism: those who use Debian really know what they&apos;re doing, but for that reason it&apos;s hard to find support for it.&lt;/p&gt;
&lt;p&gt;Also, I knew that Ubuntu came with a lot of the things I would need preinstalled.&lt;/p&gt;
&lt;p&gt;So I downloaded Ubuntu Server, reformatted my install USB, and started again!&lt;/p&gt;
&lt;p&gt;This time I discovered the ifconfig command really quickly on support sites, and was very soon able to use SSH to log in to my new server. That was an amazing moment for me!&lt;/p&gt;
&lt;p&gt;Soon after that, I installed RSA keys and was able to log in much more easily and safely to my device. I went on to install &amp;amp; configure my AMP stack, and this time view my Hello World app right from my laptop.&lt;/p&gt;
&lt;p&gt;I uploaded a website I&apos;d been building that would serve as a replacement for our current iCloud workflow, which required a few tweaks to permissions again (creating a group for www-data user as I mentioned above) and could access the site.&lt;/p&gt;
&lt;h2&gt;Port Forwarding &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#port-forwarding&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I went into my router&apos;s control panel and turned on port forwarding for my web&apos;s default port. Almost instantly, I saw my website ... but could no longer access my routers control panel!!&lt;/p&gt;
&lt;p&gt;This suits me for now, because I don&apos;t have to worry about any other ports being open, and so there&apos;s also less vulnerability for my router. But I also don&apos;t have SSH access from outside my home network.&lt;/p&gt;
&lt;p&gt;I&apos;m going to fix this, but I don&apos;t currently have the headspace to go in and configure it and my port forwarding again.&lt;/p&gt;
&lt;h2&gt;Changing IP Addresses &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#changing-ip-addresses&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At first, I was amazed at how quickly my home IP address would change. I realised that if I wanted to reduce outages, I needed a method to keep my site live enough for it to be useful by our family.&lt;/p&gt;
&lt;p&gt;I now have a free plan with No-Ip, which has installed a program on my server to update a DNS server when my IP address changes. If this works successfully, I will likely get a paid pla, unless I can figure out how to do it myself!&lt;/p&gt;
&lt;h3&gt;Fourth hitch: Uploading files using the CMS &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#fourth-hitch:-uploading-files-using-the-cms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I how had a working website I could use and refine. But when I tried to upload files I came across an error where the file could be uploaded, but couldn&apos;t be moved to the content directory.&lt;/p&gt;
&lt;p&gt;Again, this was a permissions issue I had, where I needed to give the &quot;nobody&quot; user the ability to write to disc. I did this by adding this account to the user group I had created earlier.&lt;/p&gt;
&lt;h2&gt;Conclusions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-i-created-my-home-web-server/#conclusions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Setting up my own home server has been frustrating at times, but its taught me a lot about the underlying technologies of the web.&lt;/p&gt;
&lt;p&gt;Given that, I&apos;d really like to try other projects in the future that will help me to better understand how browsers and the internet work.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How I Created My Home Web Server&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How I&apos;m doing</title><link>https://deliciousreverie.co.uk/posts/how-im-doing/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/how-im-doing/</guid><description>I very rarely write personal articles on this blog. But with the demise of social media I&apos;ve decided to share a little more of my personal thoughts here.</description><pubDate>Sun, 10 Mar 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I very rarely write personal articles on this blog. But with the demise of social media I&apos;ve decided to share a little more of my personal thoughts here.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The main motivation is that I&apos;ve discovered a lovely network of personal blogs, and even &lt;a href=&quot;https://ooh.directory/&quot;&gt;a decent directory&lt;/a&gt;. The articles I&apos;ve found myself enjoying more than I thought I would have been the personal ones.&lt;/p&gt;
&lt;p&gt;I guess this started happening a lot more when &lt;a href=&quot;https://thehistoryoftheweb.com/remembering-molly-one-of-the-greats/&quot;&gt;Molly passed away&lt;/a&gt;. I enjoyed reading other people&apos;s tributes to her, and found how similar they were to my own few experiences with this incredibly inspiring individual.&lt;/p&gt;
&lt;h2&gt;Career change&lt;/h2&gt;
&lt;p&gt;I&apos;ve also reached a bit of an inflection point in my career. I have a stable job with a company that&apos;s ... ok, actually. It&apos;s never going to set the world alight, but frankly neither is my code. I&apos;ve long thought that it&apos;s not &lt;em&gt;what&lt;/em&gt; you do for a job that matters as much as &lt;em&gt;who you do it for&lt;/em&gt;. And this company is pretty accommodating and stable.&lt;/p&gt;
&lt;p&gt;So along with that, my need to prove myself has diminished. I&apos;m not saying I&apos;m confident, but in terms of my ability or desire to succeed, I think I&apos;m at the apex of this particular career. I&apos;m always planning for the future and have some objectives that will change things up a bit and help me to remain comfortably uncomfortable, as I&apos;m still trying to live by the saying &quot;if you&apos;re the smartest person in the room, you&apos;re in the wrong room.&quot;&lt;/p&gt;
&lt;h2&gt;Relocating to North Wales&lt;/h2&gt;
&lt;p&gt;We had an unexpected visit from our landlord about 3 months ago that meant we had to move out. My wife and I were sad about that because we had only just got settled, having been there for only 2 years.&lt;/p&gt;
&lt;p&gt;We don&apos;t have the circumstances to buy, and rents were going up crazy amounts in Hertfordshire.&lt;/p&gt;
&lt;p&gt;So we decided, very quickly, to move up to Flintshire in North Wales.&lt;/p&gt;
&lt;p&gt;This was motivated largely by our kids: They&apos;ve been learning Welsh for around 3 years. This is something they&apos;ve decided to do of their own volition, but we wanted to support it as well, and give them an opportunity for future careers in the language if they wanted to.&lt;/p&gt;
&lt;p&gt;We upped and moved in six weeks. It was terrifying, exhausting, and extremely stressful. We also had to get rid of a lot of personal belongings. But the result has made a huge positive difference to the well being of the whole family.&lt;/p&gt;
&lt;p&gt;So we&apos;re all learning Welsh now. And enjoying it here immensely.&lt;/p&gt;
&lt;h2&gt;Writing blogs is hard when you prioritise your family&lt;/h2&gt;
&lt;p&gt;The other reason I&apos;ve neglected this blog a little is that writing this blog in Markdown, committing it to the repo and publishing it, even without my revision flow, is slow and cumbersome.&lt;/p&gt;
&lt;p&gt;When I&apos;m not working I&apos;m 100% focussed on the family, which means I don&apos;t get much time to myself at all. That&apos;s a personal choice, but it has consequences. I want to be part of their lives forever, not just for a few years, so I have to take care to maintain my relationships with each of them now.&lt;/p&gt;
&lt;p&gt;I do wish there was a better workflow. I want one of those apps that integrates with my workflow so I can write articles on my phone, much like you can do with the &lt;a href=&quot;https://apps.apple.com/gb/app/wordpress-website-builder/id335703880&quot;&gt;WordPress app&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I just tried writing Markdown with the GitHub app. It&apos;s not the solution I was hoping it might become sadly. At least not yet.&lt;/p&gt;
&lt;p&gt;But the infrastructure is dead simple. I&apos;m not going to beat markdown on a static site. So it looks like there&apos;s no good way to beat that friction currently.&lt;/p&gt;
&lt;p&gt;Shame. But also, not a big deal really.&lt;/p&gt;
&lt;h2&gt;Fin&lt;/h2&gt;
&lt;p&gt;There you go. I have a few more things I wanted to say, but I&apos;ll leave it here: it&apos;s late, the children are finally asleep so I have ten minutes of doom scrolling before bed.&lt;/p&gt;
&lt;p&gt;How are you doing?&lt;/p&gt;
&lt;p&gt;PS I&apos;m building a comment section, so when that&apos;s live I hope it&apos;ll give people an opportunity to reply.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How I&apos;m doing&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How to resolve Jest issues: tests passing, but code coverage fails!</title><link>https://deliciousreverie.co.uk/posts/how-to-resolve-jest-issues-tests-passing-but-code-coverage-fails/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/how-to-resolve-jest-issues-tests-passing-but-code-coverage-fails/</guid><description>Today I&apos;m continuing with my trend of making silly mistakes so you don&apos;t have to. The subject today is asynchronous tests in Jest. I&apos;ve spent too much time on this one, and I don&apos;t want you to have the same trouble! </description><pubDate>Mon, 02 Aug 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Today I&apos;m continuing with my trend of making silly mistakes so you don&apos;t have to.&lt;/p&gt;
&lt;p&gt;The subject today is asynchronous tests in Jest. I&apos;ve spent waay too much time on this one, and I don&apos;t want you to have the same trouble.&lt;/p&gt;
&lt;p&gt;I&apos;m testing whether a page renders or not. The page takes some time to contact an API and therefore to render, so I&apos;ve used the waitFor helper in Jest to assert what should happen.&lt;/p&gt;
&lt;p&gt;As I&apos;ve mentioned the test setup is slightly immaterial, however I&apos;m writing this rather quickly before the kids get hungry.&lt;/p&gt;
&lt;p&gt;Here&apos;s the test using waitFor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;renders the page&quot;, () =&amp;gt; {
  render(
    &amp;lt;ThemeProvider theme={defaultTheme}&amp;gt;
      &amp;lt;MockedProvider mocks={mocks} addTypename={false}&amp;gt;
        &amp;lt;IndexPage /&amp;gt;
      &amp;lt;/MockedProvider&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  );

  waitFor(() =&amp;gt; {
    expect(screen.getByText(/messages/i)).toBeInTheDocument();
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anything wrong with this test? No?&lt;/p&gt;
&lt;p&gt;Look again. The documentation in fact plainly says this at the top of the page:&lt;/p&gt;
&lt;p&gt;&quot;The async methods return Promises, so be sure to use await or .then when calling them.&quot;&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://testing-library.com/docs/dom-testing-library/api-async/&quot;&gt;https://testing-library.com/docs/dom-testing-library/api-async/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What this doesn&apos;t do is show up in your tests. It&apos;ll look like they&apos;ve passed!&lt;/p&gt;
&lt;p&gt;The only reason I came across it was because when I use --codeCoverage to make sure I&apos;ve covered all of my code with tests, it shows up as uncovered lines. But also, you&apos;ll notice there is an obscure message in the terminal about this too:&lt;/p&gt;
&lt;p&gt;ReferenceError: You are trying to access a property or method of the Jest environment after it has been torn down.&lt;/p&gt;
&lt;p&gt;Basically the assertion cannot be verified because it&apos;s no longer there, the render phase has passed. Although why this results in passing tests is anybody&apos;s guess. (Please let me know in the comments if you know!).&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of a working test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;renders the page&quot;, async () =&amp;gt; {
  render(
    &amp;lt;ThemeProvider theme={defaultTheme}&amp;gt;
      &amp;lt;MockedProvider mocks={mocks} addTypename={false}&amp;gt;
        &amp;lt;IndexPage /&amp;gt;
      &amp;lt;/MockedProvider&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  );

  await waitFor(() =&amp;gt; {
    expect(screen.getByText(/messages/i)).toBeInTheDocument();
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There you go, I&apos;ve wasted hours of my precious life so you (hopefully!) don&apos;t have to!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How to resolve Jest issues: tests passing, but code coverage fails!&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How Website Forms Work</title><link>https://deliciousreverie.co.uk/posts/how-website-forms-work/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/how-website-forms-work/</guid><description>HTML Forms are one of the most fundamental elements in the web builder&apos;s toolbox. It would be a rare website that doesn&apos;t use this element to complete any of the most basic tasks someone might want to achieve. But there&apos;s a lot that goes on behind the scenes. Here&apos;s a bit more of an in-depth look at what happens when you click &apos;Send&apos; on a form, and why you need some server-side code (somewhere) to handle that process. </description><pubDate>Fri, 06 Jul 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;HTML Forms are one of the most fundamental elements in the web builder&apos;s toolbox. It would be a rare website that doesn&apos;t use this element to complete any of the most basic tasks someone might want to achieve. But there&apos;s a lot that goes on behind the scenes. Here&apos;s a bit more of an in-depth look at what happens when you click &apos;Send&apos; on a form, and why you need some server-side code (somewhere) to handle that process.&lt;/p&gt;
&lt;p&gt;First of all, we need a short history lesson about the form HTML element.&lt;/p&gt;
&lt;h2&gt;The Origin of Forms &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#the-origin-of-forms&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &amp;lt;form&amp;gt; element was originally intended to be used to post content directly to web pages. So a person visiting a web page could modify what they found there, adding to it, removing parts or all of it entirely.&lt;/p&gt;
&lt;p&gt;This feature never made it out of the lab. But it was a pretty neat, if not idealistic, concept. Imagine how different things would be if anyone could change your website. This kind of idea was eventually tried out by a few intrepid individuals, notably Wikipedia, who were allowed to edit pages without any checks or balances. Unfortunately it led to widespread abuse. The sites were often either shut down or restricted after a short period of time.&lt;/p&gt;
&lt;p&gt;But the basic need still remained: as a website visitor I want to send my email address so that the website owner can contact me is the core one. There are many others: making a purchase, submitting a comment for display on the website, purchase an item, and more.&lt;/p&gt;
&lt;p&gt;People realised that they needed some method of collecting the data people had entered on their forms. They needed to validate it, making sure fields were filled out and not left blank, that they used a number where that was expected, a date when that was required etc. Then they needed to send that to an email address or other consumer so the person could be responded to, their comment displayed (or not), their order fulfilled, or whatever.&lt;/p&gt;
&lt;p&gt;That requirement led to the necessity for server-side code.&lt;/p&gt;
&lt;h2&gt;Server Side Form Validation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#server-side-form-validation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the earliest and most popular languages to tackle this issue was PHP. You could include some server-side code right there on the page. Now when someone clicked &quot;send&quot; or &quot;submit&quot;, the PHP would collect the data, format it as an email, then send it.&lt;/p&gt;
&lt;p&gt;It did this by the form&apos;s method and action attributes. The method was usually a POST request, a new entry to be added. The action attribute could be set as a URL, either the page you were on, or another page, that when sent could be triggered.&lt;/p&gt;
&lt;p&gt;Then the PHP would receive that into the global $_POST variable, where it could be processed.&lt;/p&gt;
&lt;p&gt;Now people could not only read messages from the website as if the person had merely sent them an email, but they could check the form had been filled out properly too. You didn&apos;t have to send it to an email address though. You could accept a payment, submit a comment for approval via a CMS, or connect with a CRM to collect and analyse leads.&lt;/p&gt;
&lt;p&gt;But these all required server-side code. Some application or at least a single function that would start running when the form action was triggered by someone pressing &quot;submit&quot;.&lt;/p&gt;
&lt;p&gt;Great! Unless you have no server-side code.&lt;/p&gt;
&lt;h2&gt;JAMStack Caveat &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#jamstack-caveat&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Going &quot;Serverless&quot;, using the &quot;JAMStack&quot; means, in effect, going back to the roots of the web. We are approaching websites once more as we did before we had server-side code.&lt;/p&gt;
&lt;p&gt;Though this has huge benefits, there are new (and perhaps old) issues to overcome. The primary one being, &quot;how do we validate, process and send form data?&quot;&lt;/p&gt;
&lt;h2&gt;Processing Forms Using Client-Side Code &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#processing-forms-using-client-side-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some people realised that once you submit code to your server, you can gain access to that server remotely and do nefarious things to it. This was entirely dependent on how well people had written their server code, but there were often flaws to be found and exploited.&lt;/p&gt;
&lt;p&gt;This resulted in website owners starting to validate code on the client using JavaScript before the form data was submitted. Now form submissions could be more sanitised before they got to the server, and also feedback for the person filling out the form could be much faster. You could tell them it wasn&apos;t valid as they were typing, instead of waiting until after they clicked &quot;send&quot;.&lt;/p&gt;
&lt;p&gt;You might think we can therefore use client-side JavaScript to submit form data. But the trouble with client-side code is that it is all public. And therefore it can be manipulated by someone to perform actions you don&apos;t want to happen. For instance, someone could change the URL, and they might get lucky, hitting another API endpoint that exposes a vulnerability. Or they could modify the form data to contain malicious code that takes control of your computer via your email client.&lt;/p&gt;
&lt;p&gt;That would be bad.&lt;/p&gt;
&lt;p&gt;So we still need some way of submitting code via a private resource. Something that isn&apos;t visible or accessible by everyone.&lt;/p&gt;
&lt;h2&gt;Form Submission Without a Server &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#form-submission-without-a-server&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We currently have a few options when it comes to serverless form processing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use a 3rd party service&lt;/li&gt;
&lt;li&gt;Use a 3rd party function&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It&apos;s all about how much control you want, budget constraints you have and what you need to achieve.&lt;/p&gt;
&lt;h3&gt;Popular 3rd Party Services &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#popular-3rd-party-services&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Even before the advent of serverless, there were 3rd parties that offered form processing as a service. These are usually paid-for, often use their own form code (with varying standards), often with a version you can modify via their parameters or CSS rules.&lt;/p&gt;
&lt;p&gt;Wufoo is probably the oldest of these. But there&apos;s also Typeform, Mailchimp and others. If you use Netlify, you can use their Forms system, for which implementation is extremely easy.&lt;/p&gt;
&lt;p&gt;However there are often restrictions with these services. Some plans begin to get expensive quickly. Other features you may require, for example, sending a receipt confirmation to the sender, are impossible, or are branded with the providers&apos; details.&lt;/p&gt;
&lt;p&gt;Another option is to write your own function.&lt;/p&gt;
&lt;h3&gt;3rd Party Functions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#3rd-party-functions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With the advent of Serverless, you can now submit your form to a URL that contains a form processor running in isolation from any other code, inside its own &quot;container&quot;, or virtual server instance.&lt;/p&gt;
&lt;p&gt;Amazon Web Services have their popular version called Lambda. Microsoft has Azure. There&apos;s also Heroku, owned by Salesforce. All of these provide an environment of server side code of your choice (PHP, Node, Go etc, with different release versions). This means you can write your own form processor, specify your environment language and any variables you want to utilise, and then deploy it.&lt;/p&gt;
&lt;p&gt;This allows you complete control over everything you might want to do with your form. An added bonus is that it is run independently of the website (so no application load time for your users), and can be modified independently at any time.&lt;/p&gt;
&lt;p&gt;In this case the form method will still be POST, but the action parameter will be the URL of the serverless function.&lt;/p&gt;
&lt;p&gt;Often these services are low-cost, meaning you only pay for the server time you actually use. Processing time is often sandboxed to 1 minute (probably much more than enough to interfere with this use case), and there is no server infrastructure to worry about.&lt;/p&gt;
&lt;p&gt;An extra benefit is that if form submissions become extra busy, with concurrent POST requests, the function container will be duplicated enough times to meet demand. Automatically.&lt;/p&gt;
&lt;p&gt;It&apos;s out of scope for this article how to write a serverless function. However, it&apos;s just a normal piece of code that you might want to implement anywhere.&lt;/p&gt;
&lt;h2&gt;Form The Future &lt;a href=&quot;https://deliciousreverie.co.uk/posts/how-website-forms-work/#form-the-future&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Form have had a long history on the web. Now we&apos;re approaching things from a serverless perspective, we can address the needs of people who use our websites in a much more robust way by abstracting away what holds them back from interacting with our site.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How Website Forms Work&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Improving Performance on Deliciousreverie.co.uk</title><link>https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/</guid><description>I can&apos;t possibly think I could get away with a post about performance when my own site wasn&apos;t as spot-on as I could get. So I&apos;ve been spending a bit of time investigating how to reduce my load times. This is what I found out and how I implemented it.</description><pubDate>Tue, 06 Oct 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I can&apos;t possibly think I could get away with &lt;a href=&quot;https://deliciousreverie.co.uk/post/why-performance-matters/&quot;&gt;a post about performance&lt;/a&gt;when my own site wasn&apos;t as spot-on as I could get. So I&apos;ve been spending a bit of time investigating how to reduce my load times. This is what I found out and how I implemented it.&lt;/p&gt;
&lt;p&gt;I built this site in December 2014 when I was on paternity leave. Now that my little bairn is finally sleeping through the night I can look at my code again through significantly less zombified eyes. I found a few things I&apos;d like to improve on, namely:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reduce unused DOM elements&lt;/li&gt;
&lt;li&gt;Use non-blocking HTTP requests (at least on the home page)&lt;/li&gt;
&lt;li&gt;Streamline my CSS&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s break down each of these and see what can be done to improve them.&lt;/p&gt;
&lt;h3&gt;Reducing unused DOM elements &lt;a href=&quot;https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/#reducing-unused-dom-elements&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When I built this site, I originally envisioned a main (central) content area, with sidebars of supplementary content. But at the time, I was beginning to realise &lt;a href=&quot;https://deliciousreverie.co.uk/posts/normal-people-dont-understand-sidebars&quot;&gt;that normal people don&apos;t understand sidebars&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have since decided to just focus on delivering good content in a branded experience. That&apos;s the core of what I wanted to achieve here.&lt;/p&gt;
&lt;p&gt;So the sidebar elements are gone, along with their CSS.&lt;/p&gt;
&lt;h3&gt;Use non-blocking HTTP requests &lt;a href=&quot;https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/#use-non-blocking-http-requests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My CSS is pretty important to the experience of the site, but I&apos;m aware that by linking to an external stylesheet in the header was creating an extra round-trip for the browser before the content could be rendered.&lt;/p&gt;
&lt;p&gt;Since my CSS is pretty lean on this project (119 lines, 2kb uncompressed or minified), I opted not to use a Taskrunner tool but instead use a PHP include instead:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;style&amp;gt;&amp;lt;?php include &apos;assets/css/deliciousreverie.min.css&apos;; ?&amp;gt;&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have a unique header file for the home page, so on my other pages, the header still renders via &apos;perch_get_css&apos; as normal.&lt;/p&gt;
&lt;p&gt;If my CSS code was much larger, I would find a tool to identify all necessary &quot;above-the-fold&quot; css and abstract that out into a separate CSS file for inclusion.&lt;/p&gt;
&lt;h3&gt;3. Streamline CSS &lt;a href=&quot;https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/#3.-streamline-css&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have tended to use normalize.css to standardise delivery of my site across different browsers, just because I thought that&apos;s what everybody else did.&lt;/p&gt;
&lt;p&gt;However, after having a chat to someone I admire, I realised that following the crowd in this instance was costing me in terms of performance.&lt;/p&gt;
&lt;p&gt;When you include something like normalise.css, you&apos;re adding extra work for the browser to parse, often without rendering much of it. And you&apos;re potentially adding an extra CSS overwrite to your styles. For instance, if you want a unique checkbox input style, normalise already includes one extra style. So now we have 3 different renders: 1) The User Agent (browser) styles, 2) Normalize styles, 3) Your styles.&lt;/p&gt;
&lt;p&gt;I&apos;ve made a promise with myself to look at the source code of these libraries before I make any assumptions about what I should include in my builds in the future!&lt;/p&gt;
&lt;h3&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/improving-performance-on-delicious-reverie/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve gained some great results from these excercises:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Seeing this result on &lt;a href=&quot;https://www.webpagetest.org/&quot;&gt;Web Page Test&lt;/a&gt; was a big encouragement to me:- I had achieved much of what I wanted to.&lt;/p&gt;
&lt;p&gt;I&apos;m especially proud of the fact that I have no images at all on my home page. The render chart is similarly encouraging:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;As you can see here, I have made some huge sacrifices for including three Google fonts. On the other hand, I have had a big win with regards to images, it feels nice to splash out a bit on this form of branding.&lt;/p&gt;
&lt;p&gt;So, great. For my next challenge I want to see what I can do to streamline Wordpress. Whilst I don&apos;t think you can ever get as good results from that CMS as Perch allows you to, I&apos;m sure I can think of some ways of getting better results.&lt;/p&gt;
&lt;p&gt;What results have you had from taking a closer look at the performance of your site? I&apos;d love to hear what you come up with - please tweet me on &lt;a href=&quot;https://twitter.com/muzzlehatch_&quot;&gt;@muzzlehatch_&lt;/a&gt;!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Improving Performance on Deliciousreverie.co.uk&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Introducing Webiny Enterprise CMS+</title><link>https://deliciousreverie.co.uk/posts/introducing-webiny-enterprise-headless-cms-plus/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/introducing-webiny-enterprise-headless-cms-plus/</guid><description>Originally published for the Webiny community and Product Hunt launch. It introduces a new marketing term that was conjured up to provoke discussions about what value Webiny brings to organizations apart from the CMS aspect, and because of it&apos;s serverless nature.</description><pubDate>Wed, 12 Oct 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;We have struggled for a long time on the question of how to position Webiny as a product. From the start, it was clear that we we were building was a feature-rich product that didn&apos;t quite sit firmly in the camp of &quot;Headless CMS&quot;, yet that is what the majority of our users focused in on.&lt;/p&gt;
&lt;p&gt;As a result, we have doubled down on that particular marketing, building more features and explanation around the concept of Webiny as a Headless CMS. However, as I&apos;m sure you realize, it&apos;s an extremely crowded space. And we still didn&apos;t quite fit in.&lt;/p&gt;
&lt;p&gt;You see, Webiny isn&apos;t just one product. It&apos;s four fundamental ones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content management system&lt;/li&gt;
&lt;li&gt;File storage and Management&lt;/li&gt;
&lt;li&gt;No-code Web Page Builder&lt;/li&gt;
&lt;li&gt;Form builder&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, it&apos;s actually more than even those products: Not every CMS can scale to tens of thousands of users, serve content for millions of website pages and application interfaces, and be as easily customizable as Webiny is.&lt;/p&gt;
&lt;p&gt;We frequently hear about teams using Webiny in unique and different ways: from wealth management portals, to BitCoin exchanges, to online course providers, to customer service desks, to a no-code form builder. It&apos;s been amazing to watch different teams do all of this with Webiny and more.&lt;/p&gt;
&lt;p&gt;Not every CMS can become all — or any — of these things.&lt;/p&gt;
&lt;p&gt;But if we were to look at the terminology around the kind of tools on the market that have those features ... well, that&apos;s not what we want to be either.&lt;/p&gt;
&lt;h2&gt;What if You Need More Than a Headless CMS?&lt;/h2&gt;
&lt;p&gt;Products that we could compete with that are more than a CMS are varied, but the basic premise is the same: allow large, complex, multinational organizations with content creators in different locations and languages to populate their websites with products, blog posts and other information on a regular basis. This might not just involve writing content, but also creating short-lived product launch pages, gaining feedback using a web-based form, or sharing product literature with vendors and potential contractors.&lt;/p&gt;
&lt;p&gt;Popular acronyms in this space are WCM (&quot;Web Content Manager&quot;), and DXP (&quot;Digital Experience Platforms&quot;).&lt;/p&gt;
&lt;p&gt;These systems are often very clunky, with difficult-to-navigate interfaces. Customization options are very limited, and they often require hiring specialized individuals to manage them. Not to mention the fact that they&apos;re nearly always hugely expensive SAAS platforms that exist outside the organization&apos;s perimeter.&lt;/p&gt;
&lt;p&gt;With the features we have, it would be possible to become a competitor in this space. But Webiny is much more nimble, adaptable and on top of that, is also self-hosted.&lt;/p&gt;
&lt;h2&gt;By Any Other Name&lt;/h2&gt;
&lt;p&gt;So we stopped and asked ourselves: do tools to manage content on the web really need more acronyms? CMS is a pretty good fit, and even the &quot;headless&quot; part seems to be on it&apos;s way to being generally accepted.&lt;/p&gt;
&lt;p&gt;And, we thought, if we adopt that terminology, not only would we be alienating the fact that medium and small businesses can also depend on Webiny for their content, other teams might start to get the impression that we are also a clunky system that&apos;s prohibitively expensive, not customizable, and overall difficult to use.&lt;/p&gt;
&lt;p&gt;No, we don&apos;t want to pigeonhole ourselves like that. Yet, Webiny is more than a CMS.&lt;/p&gt;
&lt;h2&gt;An All-in-One Solution&lt;/h2&gt;
&lt;p&gt;So we&apos;re sticking with &quot;Headless CMS&quot; as a description of Webiny. But opening a conversation on that front can sometimes present a problem: what if the organization approaching us doesn&apos;t have the requirement of a content store. Perhaps instead they&apos;re looking for a way to dynamically populating content from the CMS into a &lt;a href=&quot;https://www.webiny.com/docs/overview/applications/page-builder&quot;&gt;no-code page builder&lt;/a&gt;? Or Perhaps they need an &lt;a href=&quot;https://www.webiny.com/docs/overview/applications/apw&quot;&gt;advanced publishing workflow&lt;/a&gt; for their teams to be able to create, edit, update, approve, and publish their content?&lt;/p&gt;
&lt;p&gt;Starting off the conversation &quot;We&apos;re a Headless CMS&quot; unfortunately leaves out all of the other cool features we&apos;ve built, or are planning to build in the next few months.&lt;/p&gt;
&lt;p&gt;These new features will enable organizations to build dynamic content quickly, ahead of the competition, without the involvement of technical teams past the initial setup, and do so at a fraction of the cost of other providers. And to do it at the scale that enterprise organizations require.&lt;/p&gt;
&lt;p&gt;Yes, for those organizations who need that kind of power, Webiny is an all-in-one solution that might be able to replace several costly products that are strung together with digital duct tape, not designed to work together, and are also individually difficult to work with.&lt;/p&gt;
&lt;p&gt;For those people, Webiny isn&apos;t just a CMS. It&apos;s a CMS Plus.&lt;/p&gt;
&lt;h2&gt;What Is Webiny Enterprise Headless CMS+?&lt;/h2&gt;
&lt;p&gt;Plus.&lt;/p&gt;
&lt;p&gt;It&apos;s succinct, minimalistic, but covers a lot of stuff. Webiny is still a Headless CMS. But it&apos;s a Headless CMS Plus all of the other features we&apos;ve talked about.&lt;/p&gt;
&lt;p&gt;Adding the word &quot;Plus&quot; to the business and enterprise products opens the discussion around Webiny&apos;s other capabilities. It allows us to showcase other features whilst not detracting from the core use that many of our users see value in.&lt;/p&gt;
&lt;p&gt;It allows us to start more conversations around the functionality of the Page Builder and Form Builder tools, tools that are going to get some pretty interesting added features in coming months.&lt;/p&gt;
&lt;p&gt;We&apos;re really hoping that this new initiative will allows us to open more discussions with organizations looking for, not just a Headless CMS, but something that can solve a lot of the issues around producing content in their business.&lt;/p&gt;
&lt;p&gt;If this is you, please reach out to us! We&apos;d love to hear about the difficulties you&apos;re facing. For our Enterprise customers we provide SLA support and consultancy services tailored to help your teams to build a solution that&apos;ll be designed around your workflow and specific needs. And we can build custom SSO implementations with your IDP of choice.&lt;/p&gt;
&lt;p&gt;If you&apos;re an agency or a smaller business, Webiny is a great fit for you! Use it as a Headless CMS with multi-tenancy built-in, and pay a reasonable cost for each user seat. And add-on optional benefits such as our advanced publishing workflow and headless pages.&lt;/p&gt;
&lt;p&gt;Remember, Webiny is an open-source, self-hosted solution: you can use it at no cost under our MIT license today. We hope this will help you to build smaller projects and thoroughly investigate it&apos;s capabilities before you make a decision.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Introducing Webiny Enterprise CMS+&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Introspection: comparing myself to others, glorifying the past</title><link>https://deliciousreverie.co.uk/posts/introspection-comparing-glorifying-the-past/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/introspection-comparing-glorifying-the-past/</guid><description>This post is a little bit of introspection for me. I&apos;ve recently been given a bit of time to think about certain things, and I&apos;d like a make a note of them for my own personal growth. </description><pubDate>Sat, 18 Sep 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;This post is a little bit of introspection for me. I&apos;ve recently been given a bit of time to think about certain things, and I&apos;d like a make a note of them for my own personal growth.&lt;/p&gt;
&lt;p&gt;If this isn&apos;t the kind of article you&apos;re looking for from me, feel free to skip this one.&lt;/p&gt;
&lt;h2&gt;Drawing personal comparisons &lt;a href=&quot;https://deliciousreverie.co.uk/posts/introspection-comparing-glorifying-the-past/#drawing-personal-comparisons&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I find that I often spend time thinking about who I compare to other developers, especially those on my team or within my organisation.&lt;/p&gt;
&lt;p&gt;This helps me sometimes because once I&apos;ve identified who I think is a better developer, I tend to watch their work more closely. This is because I&apos;d like to pick up some of the things that make them so effective at their jobs.&lt;/p&gt;
&lt;p&gt;However, I&apos;ve also noticed that sometimes it leads to bumps in the road of my relationships with that person. I sometimes have a tendency to say things like &quot;oh, you&apos;re a much better developer than me&quot;, which (a) puts them on a pedestal, and (b) sometimes annoys them.&lt;/p&gt;
&lt;p&gt;It puts them on a pedestal because really what I&apos;m doing is being a bit envious of that person. I&apos;m never happy with myself, and sometimes value myself very poorly. But sometimes that means I value others too much.&lt;/p&gt;
&lt;p&gt;It annoys my colleagues because once I&apos;ve told them I think they&apos;re a better developer than me, they have to find some way of shifting the rather uncomfortable focus either back to me or onto something else. For some people that&apos;s a frustration they could do without.&lt;/p&gt;
&lt;p&gt;How am I going to tackle this? I&apos;m going to try to remind myself that a team is made up of a spectrum of skill sets and abilities. &lt;a href=&quot;https://youtu.be/Vyn_xLrtZaY&quot;&gt;There are no super chickens&lt;/a&gt;, and everyone is of value and creates value for the organisation.&lt;/p&gt;
&lt;h2&gt;Glorifying the past (or not) &lt;a href=&quot;https://deliciousreverie.co.uk/posts/introspection-comparing-glorifying-the-past/#glorifying-the-past-(or-not)&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used to often imagine myself living in some era of the past. Especially in England during the early 1800s, when John Keats was walking the Lakes or composing his best poetry. I enjoyed thinking of the &quot;simplicity&quot; of the age, in terms of there not being nearly so much commercialism, lack of common good, and general trust between individuals than perhaps there can be in todays&apos; world.&lt;/p&gt;
&lt;p&gt;However, somewhere in the past 10 years I&apos;ve stopped doing that.&lt;/p&gt;
&lt;p&gt;I think I finally decided that although those times in the past might have had some advantages, there were no doubt many hardships and disadvantages that caused a huge amount of suffering. Generally much poorer and less available healthcare. Rampant racism and sexism. Even perhaps a sort of brutality that our current age doesn&apos;t demonstrate so readily.&lt;/p&gt;
&lt;p&gt;But also, I stopped reading. I&apos;ve simply had no time available to do so.&lt;/p&gt;
&lt;p&gt;For both of these reasons, I stopped imagining myself in those past times. But the result of that is that I&apos;ve felt more adrift.&lt;/p&gt;
&lt;p&gt;Having that imagined world to think back on helped me to retreat away from some of the more uncomfortable realities I faced: disappointments, difficult and long commutes, the times I didn&apos;t enjoy my home life or work.&lt;/p&gt;
&lt;p&gt;I wonder if I should bring back at least some of that imagination. I definitely want to bring reading for pleasure back into my life. It&apos;s going to be quite a challenge with my schedule though.&lt;/p&gt;
&lt;p&gt;How am I going to tackle this? I am going to try to spend an hour a week reading, hopefully that&apos;ll give me a little more &quot;scope for the imagination&quot; back into my life.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/introspection-comparing-glorifying-the-past/#conclusion&quot;&gt;#&lt;/a&gt;&quot;&lt;/h2&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Introspection: comparing myself to others, glorifying the past&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Is Gatsby in decline?</title><link>https://deliciousreverie.co.uk/posts/is-gatsby-decline/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/is-gatsby-decline/</guid><description>I&apos;m sure I was one of the earliest users of Gatsby. But is it in decline? The title is a bit click-baity because I don&apos;t really feel that it is. Rather, I think it&apos;s well on it&apos;s way to finding it&apos;s niche.</description><pubDate>Sat, 13 Aug 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Recently I encountered a Reddit thread along the lines of &quot;is Gatsby dead?&quot;, and even some colleagues I respect were agreeing that yes, Gatsby is in decline, suggesting there are better technologies out there.&lt;/p&gt;
&lt;p&gt;Whilst there are a good range of frameworks we can use these days, I don&apos;t necessarily agree that Gatsby is in decline and people should avoid using it.&lt;/p&gt;
&lt;p&gt;I&apos;d like to explain why.&lt;/p&gt;
&lt;h2&gt;In decline? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#in-decline&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I feel sure I was one of the earlier users of Gatsby. I was introduced to it and Netlify before v1 was released, and started experimenting with fetching data from a content source pretty soon after that.&lt;/p&gt;
&lt;p&gt;I&apos;ve since had a chance to work extensively with both Gatsby and Next.js, and can see why people like the Next.js lower-level API. I agree that for a lot of use cases, Next.js is a great tool to use. I particularly like how easy &lt;a href=&quot;https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props&quot;&gt;getServerSideProps()&lt;/a&gt; is to build an isomorphic application that runs in a container.&lt;/p&gt;
&lt;p&gt;However, when I need to build an application, there&apos;s always going to be some assessment in my head about which tool is better.&lt;/p&gt;
&lt;h2&gt;Gatsby&apos;s strengths &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#gatsby&apos;s-strengths&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Where Gatsby&apos;s niche might be in the future is one of these, or possibly all of them:&lt;/p&gt;
&lt;h3&gt;1. Developers who want to leverage the plugin ecosystem &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#1.-developers-who-want-to-leverage-the-plugin-ecosystem&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are a lot of developers who don&apos;t have the luxury of doing things in the best way possible themselves. Usually this is because of tight, perhaps unreasonable, deadlines. The plugin ecosystem doesn&apos;t always mean faster development, but it is easy to configure without having to learn APIs deeply first.&lt;/p&gt;
&lt;p&gt;This might also benefit newer users to the JavaScript ecosystem, who need a way of learning React and friends.&lt;/p&gt;
&lt;h3&gt;2. Applications that source from many different APIs &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#2.-applications-that-source-from-many-different-apis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Where Gatsby does particularly well is with the &quot;content mesh&quot;. You might have heard this referred to as &quot;composable commerce&quot; or something similar. But Gatsby had the concept very early on. The idea is that you source data from a variety of APIs which are then compiled into a &lt;a href=&quot;https://www.gatsbyjs.com/docs/tutorial/part-4/&quot;&gt;single GraphQL API&lt;/a&gt; which you can then use to build up components, then pages, and built into a website.&lt;/p&gt;
&lt;p&gt;Gatsby really excels at this. I can confidently fetch loads data from REST and GraphQL backends, query the rest in pages, components or templates, and chuck away the data I don&apos;t want when the application is built.&lt;/p&gt;
&lt;h3&gt;3. Applications that don&apos;t need specialist environments &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#3.-applications-that-don&apos;t-need-specialist-environments&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Gatsby has the &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/&quot;&gt;gatsby-ssr.js&lt;/a&gt; and &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/&quot;&gt;gatsby-browser.js&lt;/a&gt; files that both wrap the application, one that runs at build time and one on the client, as the names suggest). I think this is far superior separation of concerns than Next.js&apos; &lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-app&quot;&gt;_App.js&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It also leverages the open source react router, instead of including it&apos;s own. Next.js has admittedly &lt;a href=&quot;https://nextjs.org/blog/layouts-rfc&quot;&gt;made strides in this direction&lt;/a&gt; but until recently I had real issues when trying to render components on nested routes in a Next.js application.&lt;/p&gt;
&lt;p&gt;Also, I find the &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-image/&quot;&gt;Gatsby &amp;lt;Image /&amp;gt; element&lt;/a&gt; superior to Next.js, because it works statically, whereas you again need &lt;a href=&quot;https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features&quot;&gt;specialist infrastructure (or a containerized application)&lt;/a&gt; for Next.js&apos; to work.&lt;/p&gt;
&lt;h3&gt;4. Where you need custom data in headers, footers etc &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#4.-where-you-need-custom-data-in-headers-footers-etc&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ever tried fetching data from an API at build time (or on a server) in a component with Next.js? You either need a context object or prop drilling, which isn&apos;t always possible or desireable.&lt;/p&gt;
&lt;p&gt;Gatsby&apos;s &lt;a href=&quot;https://www.gatsbyjs.com/docs/how-to/querying-data/static-query/&quot;&gt;staticQuery()&lt;/a&gt; on the other hand works in components and makes this really easy.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/is-gatsby-decline/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Don&apos;t get me wrong, there&apos;s a huge amount going for Next.js, and I love building stuff with it. But I&apos;m still not always going to reach for it.&lt;/p&gt;
&lt;p&gt;If I need an application that requires React or a heavy amount of client-side JavaScript, I&apos;ll assess whether Gatsby is best or Next.js is.&lt;/p&gt;
&lt;p&gt;All in all, I think there was an explosion in users when Gatsby first launched, and although there are other great tools like Next.js and Astro, I think it still has a valuable place in a JavaScript developers&apos; toolbox.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Is Gatsby in decline?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Iterate.</title><link>https://deliciousreverie.co.uk/posts/iterate/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/iterate/</guid><description>A few times during the past year I&apos;ve caught myself advocating for iterative changes. Both in software and in terms of our processes I&apos;ve found that I far prefer smaller, incremental improvements over stressful, complicated and far-reaching changes that may or may not improve fulfilment, delivery and other metrics.</description><pubDate>Wed, 11 Oct 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Despite better marketing opportunities, the days of big bang releases are gone. In today&apos;s software market, iterative releases are the hallmark of good planning, well organised teams, and software stability.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A few times during the past year I&apos;ve caught myself advocating for iterative changes. Both in software and in terms of our processes I&apos;ve found that I far prefer smaller, incremental improvements over stressful, complicated and far-reaching changes that may or may not improve fulfilment, delivery and other metrics.&lt;/p&gt;
&lt;h2&gt;1. A software renovation project&lt;/h2&gt;
&lt;p&gt;The first time was during a renovation project. I had a code base that was built several years prior by someone who no longer worked at the company. Instead of scrapping the code or making huge rewrites, I decided that my first objective was to make the code more modular and easier to understand. I would also try to use what had, since the original person authored the code, become standard approaches.&lt;/p&gt;
&lt;p&gt;So I replaced classes with functions, returned new objects instead of adding to existing ones, added a library of types, and isolated code within clearly defined, modular boundaries.&lt;/p&gt;
&lt;p&gt;I knew the code needed some work. But if I were to take it further, I would be taking an extra risk: I didn&apos;t fully understand the implications of all of the paths the code could take. I didn&apos;t know the business impact of changing something that looked innocuous and yet could affect users.&lt;/p&gt;
&lt;p&gt;Later, with these improvements in place, and a better understanding of what the business needed from the code, I could initiate a larger project to rebuild it into a more suitable shape.&lt;/p&gt;
&lt;p&gt;There were also a number of other deliverables which the business needed in the interim. I was able to work on these until an opportunity came up to invest more time in improving what I had already standardised.&lt;/p&gt;
&lt;h2&gt;2. Implementing a team retrospective&lt;/h2&gt;
&lt;p&gt;I first saw the benefits of retrospectives a few years ago. When they were first implemented in my team, I saw straight away what an opportunity they created.&lt;/p&gt;
&lt;p&gt;Quickly, we were able to voice our concerns about what processes had failed during our sprints. Other team members, including project managers, were able to pinpoint issues without having to focus on an individual team member, which led to less accusative and more collaborative environment. Everyone seemed to be happier.&lt;/p&gt;
&lt;p&gt;So when I found at my new place of work we didn&apos;t have a retrospective, I was keen to get one started. I came up with a simple, straightforward board plan which consisted of electronic sticky notes for each team member, and 4 rows, one each for &quot;were the action points for the last retro done?&quot;, &quot;what worked well&quot;, &quot;what went badly&quot;, &quot;ideas&quot; and &quot;action points&quot;.&lt;/p&gt;
&lt;p&gt;When someone puts a ticket in the &quot;what went badly&quot; row, I spend a bit more time to identify exactly what caused the issue and try to turn it into an action point.&lt;/p&gt;
&lt;p&gt;In the approximately 10 months since we started, some team members have come up with really good ideas which we&apos;ve implemented. We&apos;ve eliminated a large number of things that were causing unwanted friction or frustration, and I hope it has also helped the team&apos;s wellbeing.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Iterate.&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The JavaScript CMS Landscape</title><link>https://deliciousreverie.co.uk/posts/javascript-cms-landscape/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/javascript-cms-landscape/</guid><description>I&apos;ve been keenly interested in a specific subset of CMSes since about 2 years ago, when it became clear that Zopa would soon need to invest in one, and that I would be involved in choosing something that would be a good fit for the company. This post is a roundup of some of the great products available, and is the result of some of the research and proofs-of-concept I have made. </description><pubDate>Mon, 23 Nov 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve been keenly interested in a specific subset of CMSes since about 2 years ago, when it became clear that Zopa would soon need to invest in one, and that I would be involved in choosing something that would be a good fit for the company. This post is a roundup of some of the great products available, and is the result of some of the research and proofs-of-concept I have made.&lt;/p&gt;
&lt;p&gt;The requirements I had for my search were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has to be self-hosted, open source, so we can secure our perimeter&lt;/li&gt;
&lt;li&gt;Needs to be maintainable by an internal team of JavaScript developers&lt;/li&gt;
&lt;li&gt;Needs to be headless (in order to render the frontend in an existing JavaScript application)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had a separate search for a hosted platform-as-a-service which I executed in parallel, but my personal interest is more towards the open source community, hence the focus of this blog post.&lt;/p&gt;
&lt;h2&gt;1. Ghost &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#1.-ghost&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ghost seems like the grandaddy of JavaScript CMSes. Their version 3, which was launched about a year ago, was fully API-enabled, allowing it to be used as a headless CMS.&lt;/p&gt;
&lt;p&gt;Ghost was built out of dissatisfaction with the WordPress technology stagnation if I remember correctly, but it&apos;s grown far past that to become a slick, comfortable and beautiful interface for building simple blogs.&lt;/p&gt;
&lt;p&gt;Pros: Wonderful editing experienceCons: Not very extendable in terms of custom fields and content typesLink: &lt;a href=&quot;https://ghost.org/&quot;&gt;https://ghost.org&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;2. Strapi &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#2.-strapi&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve watched Strapi go from an early Alpha state to a mature product with an international team, and that&apos;s been very rewarding to see. The UI is really great with a lot of integrations, and they&apos;re constantly working on new plugins and features that enrich your experience.&lt;/p&gt;
&lt;p&gt;I was particularly pleased that they continue to improve on their Gatsby integrations, but there&apos;s one thing that I hope they&apos;re able to resolve in the short term: transforming data in their Gatsby examples is done on the frontend, when it can be done in the Node process.&lt;/p&gt;
&lt;p&gt;Pros: Very adaptable and customizableCons: You only get 3 roles on the free tier (unless your OSS or a student), their GatsbyJS examples don&apos;t demonstrate how to transform data on the serverLink: &lt;a href=&quot;https://strapi.io/&quot;&gt;https://strapi.io&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;3: Webiny &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#3:-webiny&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Webiny is a lot more than a CMS, in fact, the CMS aspect is just one of the plugins for this incredible serverless framework. And honestly, Webiny does showcase some of the fancy things that become possible with serverless, and also levels out a lot of that road for newer developers.&lt;/p&gt;
&lt;p&gt;If you&apos;re interested in serverless architecture, definitely give Webiny a look.&lt;/p&gt;
&lt;p&gt;Pros: Serverless, so expect a lot of free hosting. A great way to learn the serverless architecture paradigmCons: Recommends use of gatsby-source-graphql plugin, which doesn&apos;t have access to nodes at build time. As a result, data transforming must be done on the frontend :-(Link: &lt;a href=&quot;https://webiny.com/&quot;&gt;https://webiny.com&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;4: Keystone &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#4:-keystone&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I haven&apos;t spent any time with Keystone, but Wes Bos hails it&apos;s solid role-based access and easy deployment features. I&apos;ll update this post when I&apos;ve had a chance to play with it.&lt;/p&gt;
&lt;p&gt;Pros: Role based access, easy deploymentCons: // TODO: try this app and update this blog postLink: &lt;a href=&quot;https://www.keystonejs.com/&quot;&gt;https://www.keystonejs.com&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;5. Apostrophe &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#5.-apostrophe&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apostrophe only just fits onto this list because it&apos;s a full-stack CMS with a Headless plugin ... which is just fine, but it&apos;s an interesting choice. The presence of plugins like this one gives me confidence this is a mature app that has a solid future. Again, I haven&apos;t tried it out, but I will report back when I have.&lt;/p&gt;
&lt;p&gt;The marketing talks about &quot;in-context editing&quot;, but I&apos;m not sure this refers to headless mode or not (edit: it&apos;s not) ... if it is, this could be a killer feature.&lt;/p&gt;
&lt;p&gt;Pros: // TODO: try this app and update this blog postCons: // TODO: try this app and update this blog postLink: &lt;a href=&quot;https://apostrophecms.com/&quot;&gt;https://apostrophecms.com&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;6. Payload &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#6.-payload&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I heard about this newcomer on the scene only a few days ago, but the value proposition made sense to me. The UI is very minimal, on the basis that you&apos;ll want to customize it significantly. Their strongest feature is that there are hooks for every action, so you can extend the functionality very easily, and the initial codebase you see is extremely minimal.&lt;/p&gt;
&lt;p&gt;Pros: // TODO: try this app and update this blog postCons: // TODO: try this app and update this blog postLink: &lt;a href=&quot;https://payloadcms.com/&quot;&gt;https://payloadcms.com&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/javascript-cms-landscape/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript, particularly on the server, is still a new language, but it&apos;s incredible that we have so much variety in tooling available to us already.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The JavaScript CMS Landscape&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How We&apos;re using JavaScript with Drupal</title><link>https://deliciousreverie.co.uk/posts/javascript-with-drupal/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/javascript-with-drupal/</guid><description>For the past 3 years I&apos;ve been working at a company that is heavily invested in Drupal as a platform. We&apos;ve gone through a massive iteration with JavaScript and believe we&apos;ve arrived at a solution that might help other teams.</description><pubDate>Thu, 21 Aug 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For the past 3 years I&apos;ve been working at a company that is heavily invested in Drupal as a platform. The company has several major sites and maintains its own Drupal extensions (plugins, or packages). In fact, it has around 40 of them.&lt;/p&gt;
&lt;p&gt;As you might imagine there&apos;s a significant amount of JavaScript for some of these, since they manage our in-house player, run live events and provide other functionality such as user dashboards and integrated quizzes.&lt;/p&gt;
&lt;p&gt;We&apos;ve gone through a massive iteration with JavaScript and believe we&apos;ve arrived at a solution that might help other teams that do similar.&lt;/p&gt;
&lt;p&gt;Additionally, this might perhaps provide a basis for further discussion about an &lt;a href=&quot;https://www.drupal.org/project/a11y_autocomplete_element/issues/3472705&quot;&gt;integrated bundler for Drupal&lt;/a&gt;, something I&apos;m keen to see happen someday. But only if &lt;a href=&quot;https://jvns.ca/blog/2024/11/18/how-to-import-a-javascript-library/&quot;&gt;native import maps don&apos;t get there first&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;History&lt;/h2&gt;
&lt;p&gt;Historically, JavaScript has been seen merely as a scripting language, meaning it didn&apos;t require types and tests, unlike PHP code which we were more keen to guarantee it did what it should be doing. This also meant there was little to no documentation for the JavaScript code.&lt;/p&gt;
&lt;p&gt;This was getting more and more difficult to work with especially since some run into thousands of lines. Implementing new business requirements was really slow and often resulted in releases which contained broken code and had to be rolled back.&lt;/p&gt;
&lt;p&gt;My task since then has been to strike a balance between releasing safely and still making it possible for our PHP-focussed developers to interact with the codebase.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;A Drupal extension helps us to encapsulate code for a specific purpose. For example if we create a new API, it will have its own extension. These extensions can tap into the existing functionality Drupal has.&lt;/p&gt;
&lt;p&gt;For that reason, JS code often needs to be written in such a way that it interacts with these APIs.&lt;/p&gt;
&lt;p&gt;This frequently means that the JS is called by Drupal, and often that takes the form of an immediately invoked function expression (IIFE), which looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;((Drupal, once) =&amp;gt; {
  Drupal.behaviors.myUniqueModuleId = {
    attach: (context, settings) =&amp;gt; {
      const [myElement] = once(&quot;my-element-id&quot;, &quot;.my-element-selector&quot;);
      const userName = settings.user.name;
      if(myElement) {
        myElement.innerText = `${userName}`;
      }
    }
  }
})(
  window.Drupal,
  window.once,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Drupal behaviours&lt;/h3&gt;
&lt;p&gt;Drupal behaviors are a part of Drupal&apos;s JavaScript API that allow developers to attach functions to be executed at specific times during a web page&apos;s lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.innoraft.ai/blog/drupal-behaviours-what-are-they&quot;&gt;This blog article has a pretty straightforward explanation of why we need to hook into those&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In brief, behaviours are for hooking into the context. For example, Drupal might update the DOM with new elements, so we can run the behaviour in conjunction with those changes, in effect, _attach_ing it to the backend changes.&lt;/p&gt;
&lt;h3&gt;Once&lt;/h3&gt;
&lt;p&gt;Once is a jQuery API that has been adopted in Drupal to ensure something runs only ... well, &lt;em&gt;once&lt;/em&gt;. It&apos;s needed because Drupal might call the code in behaviours multiple times as the page is being set up and executed, and as I mentioned before, DOM elements can be changed by Drupal after the first load. For this reason, &lt;code&gt;window.addEventListener(&apos;DOMContentLoaded&apos;, () =&amp;gt; {})&lt;/code&gt; might not be sufficient.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;once()&lt;/code&gt; no longer means we&apos;re using jQuery, it&apos;s been forked as a separate, standalone module by the Drupal community.&lt;/p&gt;
&lt;p&gt;Sometimes this isn&apos;t available to us though, for example our media player is a standalone Custom Element written with JavaScript. To ensure that the DOM element has loaded and been registered in the DOM in that case, we have &lt;a href=&quot;https://deliciousreverie.co.uk/posts/observing-element-changes-in-the-shadow-dom/&quot;&gt;a custom &lt;code&gt;waitForElement()&lt;/code&gt; JavaScript function&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Approach&lt;/h2&gt;
&lt;p&gt;When it comes to our JavaScript projects, what we have now is two parallel approaches which can be adopted when a new extension is being conceived, or later on when the need arises. If the recommendations below are adhered to the resulting code changes between the two are relatively small.&lt;/p&gt;
&lt;h3&gt;1. Minimal approach&lt;/h3&gt;
&lt;p&gt;If only minimal amounts of JS are expected, we can skip the more complex setup mentioned below and add the JS to a &lt;strong&gt;/js&lt;/strong&gt; folder and enqueue it using &lt;strong&gt;module.libraries.yml&lt;/strong&gt;. We ensure it&apos;s always defined as an ES Module so that if we need to switch to bundled code we can do so with minimal refactors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;myjs:
  js:
    js/myjs: { attributes: { type: module } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I emphasise this is for really small things like setting some text on a DOM element as in the example above. The minimal approach does not support JavaScript unit tests. However because we use JSDoc instead of TypeScript, we can still implement type safety.&lt;/p&gt;
&lt;h3&gt;2. With bundling&lt;/h3&gt;
&lt;p&gt;We&apos;ve avoided tools like &lt;a href=&quot;https://www.drupal.org/project/foxy&quot;&gt;Foxy&lt;/a&gt;, which allows us to bundle with Vite on the site level and integrates with Composer. Whilst it would be appealing from an organisational perspective to be able to have one JS bundle, it could be wasteful as we&apos;d be unable to customise that for the user. It would also likely be very large on the initial load which could delay interactivity in some cases.&lt;/p&gt;
&lt;p&gt;We do use some dependencies from &lt;a href=&quot;https://asset-packagist.org/&quot;&gt;Asset Packagist&lt;/a&gt;. These are typically large dependencies such as JSPDF and Chart.js which we use in more than one Drupal extension.&lt;/p&gt;
&lt;p&gt;When dependencies are loaded in via Asset Packagist, they are loaded in on the global object, and are accessed directly from that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
const { Chart } = window

const chart = new Chart()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Care must be maintained not to overwrite globals. Also upgrading requires extensive testing since it might be used in several modules, which means that we could easily break something.&lt;/p&gt;
&lt;p&gt;We find Asset Packagist to be problematic because packages are often not available until &lt;em&gt;after&lt;/em&gt; they have been requested for the first time. This has repeatedly led to failed builds.&lt;/p&gt;
&lt;p&gt;It&apos;s also a mirror of NPM which has more opaque security implications.&lt;/p&gt;
&lt;p&gt;Therefore, When adding external dependencies from NPM we typically switch to using a bundler for that extension.&lt;/p&gt;
&lt;p&gt;The downside of this is that we might have several versions of a specific dependency on one page, or across the whole site. But since they are encapsulated in the module they are not globally available so clashes aren&apos;t a possibility.&lt;/p&gt;
&lt;p&gt;It also means that the JavaScript bundles could be larger as a result which might have been avoided with global dependencies. However the upgrade path is much easier since dependencies are easily identifiable.&lt;/p&gt;
&lt;p&gt;If an external dependency is needed, or the complexity of the JS rises above a certain level (we haven&apos;t specified where we should switch, instead leave it up to the good judgement of each developer), we have adopted the following setup:&lt;/p&gt;
&lt;h4&gt;1. Introduction of /src and /dist folders&lt;/h4&gt;
&lt;p&gt;When someone decides to add this step, the folder structure changes to accommodate the bundling process. This also means that the libraries YML changes to&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;myjs:
  js:
    js/dist/myjs: { attributes: { type: module } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Still very similar to above.&lt;/p&gt;
&lt;h4&gt;2. Package JSON and Vite config&lt;/h4&gt;
&lt;p&gt;Next, we have a package.json with a &lt;code&gt;&quot;type&quot;: &quot;module&quot;&lt;/code&gt; declaration and some standarsised libraries such as Vite, our internal JavaScript package, and a dependency of &lt;code&gt;@rollup/plugin-replace&lt;/code&gt;, we&apos;ll see why in a minute.&lt;/p&gt;
&lt;p&gt;We also have settled on the following standardised scripts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot; : {
    &quot;lint:check&quot;: &quot;eslint -c eslint.config.js &apos;**/*.js&apos;&quot;,
    &quot;lint:fix&quot;: &quot;eslint -c eslint.config.js --fix &apos;**/*.js&apos;&quot;,
    &quot;format:check&quot;: &quot;prettier --check &apos;**/*.js&apos;&quot;,
    &quot;format:fix&quot;: &quot;prettier --write &apos;**/*.js&apos;&quot;,
    &quot;test:ci&quot;: &quot;vitest --run&quot;,
    &quot;test:watch&quot;: &quot;vitest --coverage&quot;,
    &quot;build&quot;: &quot;vite build&quot;,
    &quot;dev&quot;: &quot;vite build --watch&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these scripts a PHP developer can successfully work with JS by running &lt;code&gt;npm run dev&lt;/code&gt;, which will re-compile the JS code at every change. They an also format and check linting easily.&lt;/p&gt;
&lt;p&gt;Standardising these becomes really handy when there are sub-modules. They also help us use Git Hooks to vaildate the code before we push to our source repository.&lt;/p&gt;
&lt;p&gt;We also have a Vite setup as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/// &amp;lt;reference types=&quot;vite/client&quot; /&amp;gt;
import { defineConfig } from &quot;vite&quot;;
import { resolve } from &quot;path&quot;;
import fg from &quot;fast-glob&quot;;
import replace from &quot;@rollup/plugin-replace&quot;;

// Generate entry points dynamically
const inputFiles = fg.sync(&quot;src/**/*.js&quot;).reduce((entries, file) =&amp;gt; {
  const name = file.replace(/^src\//, &quot;&quot;).replace(/\.js$/, &quot;&quot;);
  entries[name] = resolve(__dirname, file);
  return entries;
}, {});

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    sourcemap: true,
    outDir: resolve(__dirname, &quot;dist&quot;),
    rollupOptions: {
      input: inputFiles,
      output: {
        entryFileNames: &quot;[name].js&quot;,
        chunkFileNames: &quot;[name].js&quot;,
        assetFileNames: &quot;[name].[ext]&quot;,
      },
      plugins: [
        replace({
          &quot;process.env.NODE_ENV&quot;: JSON.stringify(&quot;production&quot;),
          preventAssignment: true,
        }),
      ],
    },
  },
  test: {
    include: [&quot;tests/**/*.test.js&quot;],
    environment: &quot;jsdom&quot;,
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you might be able to follow, although the JS files are bundled they&apos;re not combined into one JavaScript file. This is because we still want our JS to be managed by Drupal. In some cases, specific JS won&apos;t be loaded onto a page. In others as I said above, the JS won&apos;t be relevant until certain Drupal behaviours have been run first.&lt;/p&gt;
&lt;p&gt;Other than that, we&apos;re using the &lt;code&gt;@rollup/plugin-replace&lt;/code&gt; to provide an environment variable of &lt;code&gt;NODE_ENV&lt;/code&gt; set to &lt;code&gt;production&lt;/code&gt;. Vitest by default injects a value of &lt;code&gt;test&lt;/code&gt;, which comes in handy for testing as you&apos;ll see in a minute.&lt;/p&gt;
&lt;h4&gt;Test approach&lt;/h4&gt;
&lt;p&gt;You&apos;ll notice in the Vitest set up above we have our test setup configuration as part of vitest.config.js, which saves an additional file.&lt;/p&gt;
&lt;p&gt;Test are located in &lt;strong&gt;src/test&lt;/strong&gt; folder exclusively to keep them out of the way of the production code.&lt;/p&gt;
&lt;p&gt;We also need to stop the IIFE from executing as soon as it&apos;s imported for the test suite, and before any test setups can be performed. This can be handled by wrapping it in this &lt;code&gt;if&lt;/code&gt; statement:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(!process.env.NODE_ENV !== &quot;test&quot;) {
  ((Drupal, once) =&amp;gt; {
    // initial setup goes here, for example timers
    Drupal.behaviors.myUniqueModuleId = {
      attach: (context, settings) =&amp;gt; {
        const [myElement] = once(&quot;my-element-id&quot;, &quot;.my-element-selector&quot;);
        const userName = settings.user.name;
        if(myElement) {
          myElement.innerText = `${userName}`;
        }
      }
    }
  })(
    window.Drupal,
    window.once,
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But ideally the code is in a separate JS module from the IIFE, since that is much neater than this solution.&lt;/p&gt;
&lt;h4&gt;Building the JS for Production&lt;/h4&gt;
&lt;p&gt;We&apos;re still in quit a bit of a quandry about how to build JS that uses the bundler approach. A few options we&apos;re considering:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build locally:&lt;/strong&gt; since we are building the /dist folder in development anyway, we can push the build code into the source repository. This is hazardous because there might be changes depending on the platform. Also if someone forgets to run build before they commit, we could have stale code in production and an unexpected outcome the next time soneone makes a code change&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build in the pipeline:&lt;/strong&gt; This ensures that the module contains the latest code in the &lt;strong&gt;/dist&lt;/strong&gt; folder. But it doesn&apos;t take into account the production environment. It also allows us to build the JS modules upfront, instead of having to do them all at once which could be time consuming&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build it when releasing:&lt;/strong&gt; Our hosting setup allows us to hook into the build before it&apos;s pushed into production. We could build the JS in the target environment at that point. However, we won&apos;t be able to automate this since we don&apos;t know which extensions need bundling.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We currently use both (1) and (2). Implementing (3) has proved to be problematic and time consuming.&lt;/p&gt;
&lt;h2&gt;How we handle sub modules&lt;/h2&gt;
&lt;p&gt;Some Drupal extensions have modules that further extend the functionality over the base implementation. They help with encapsulating other code and allow it to be called only when specifically needed.&lt;/p&gt;
&lt;p&gt;So how do we support JavaScript that appears inside these modules?&lt;/p&gt;
&lt;p&gt;Each of these sub-modules is treated as a separate JS bundle, so has it&apos;s own Vite config, package.json and all of the set up mentioned above. We have experimented with inheriting some of this config but it&apos;s proved to be more confusing. Duplicating some files is a small price to pay for simplicity.&lt;/p&gt;
&lt;p&gt;The addition of sub-modules could make things awkward when modifying code. To negate this we&apos;ve adopted an approach that hopefully speeds things up. In the root of the PHP extension we have an additional &lt;strong&gt;package.json&lt;/strong&gt; file that is very minimal. It only has one dependency and includes the following scripts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;scripts&quot;: {
    &quot;install&quot;: &quot;npm-run-all --parallel main:install sub_module:install&quot;,
    &quot;main:install&quot;: &quot;cd js &amp;amp;&amp;amp; npm install&quot;,
    &quot;sub_module:install&quot;: &quot;cd modules/sub_module/js &amp;amp;&amp;amp; npm install&quot;,
    
    &quot;dev&quot;: &quot;npm-run-all --parallel main:dev sub_module:dev &quot;,
    &quot;main:dev&quot;: &quot;cd js &amp;amp;&amp;amp; npm run dev&quot;,
    &quot;sub_module:dev&quot;: &quot;cd modules/sub_module/js &amp;amp;&amp;amp; npm run dev&quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the addition of this file, developers can run &lt;code&gt;npm run install&lt;/code&gt; from the project root and install all JavaScript dependencies for each of the sub modules. This makes development much faster and stil allows for a developer to change directory to a specific sub module if they are only dealing with one.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this has been a useful walkthrough of how we&apos;re currently using JavaScript in our Drupal sites. If you have any further questions, spot some errors or want to discuss it further, please feel free to respond using the comments section below.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How We&apos;re using JavaScript with Drupal&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>I launched a YouTube channel</title><link>https://deliciousreverie.co.uk/posts/launched-youtube-channel/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/launched-youtube-channel/</guid><description>I opened a YouTube channel! It was originally made to teach some kids coding schools, but I think it might prove to be a useful resource I&apos;ll be using more often. </description><pubDate>Mon, 30 May 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Time flies, as they say, and as I&apos;m getting older (reached 42 this year) I find I have as much to say but less time to say it. I&apos;m thinking people who are interested in my content are likely to be in the same position.&lt;/p&gt;
&lt;p&gt;With that in mind, I &lt;a href=&quot;https://www.youtube.com/channel/UCzbL3ZYvJWzJbrbHCgZLDJQ&quot;&gt;opened my own YouTube channel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s some of the things I plan to talk about:&lt;/p&gt;
&lt;p&gt;I opened the channel mostly because I wanted to &lt;a href=&quot;https://www.youtube.com/playlist?list=PLB3rPzXxlZBdnaRBZ-5bwoRA3Hrisdvmk&quot;&gt;teach my kids and others how to code, for which I&apos;ve created a playlist&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I like to review and occasionally rant about) open source CMS systems, &lt;a href=&quot;https://www.youtube.com/watch?v=QGVGRqjtx-o&quot;&gt;which I&apos;ve already created one video about&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m also mentoring a group of young adults,and &lt;a href=&quot;https://www.youtube.com/playlist?list=PLB3rPzXxlZBepLYmmRIuBPvrP4S-PnWZq&quot;&gt;although as of today there&apos;s only one video in the playlist&lt;/a&gt;, I&apos;ll be adding more videos as we bump into gaps in their knowledge that I think might be advantageous to share with the wider community.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:I launched a YouTube channel&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Learning serverless with Webiny</title><link>https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/</guid><description>Webiny is a new CMS in the market, one that seeks to compete with other well-established headless CMS platforms and existing apps. But I&apos;ve also come to enjoy using it for another reason... </description><pubDate>Fri, 03 Apr 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Webiny is a new CMS in the market, one that seeks to compete with other well-established headless CMS platforms and existing apps. But I&apos;ve also come to enjoy using it for another reason: it&apos;s helping me learn how to apply principles of Serverless application architecture.&lt;/p&gt;
&lt;p&gt;I&apos;ve spent a great deal of time over the past 2 years around the CMS space. I am particularly focused in self-hosted, javascript-based CMSes. I had already created sites with both Ghost and Strapi, two very competent systems with content APIs.&lt;/p&gt;
&lt;p&gt;Very recently, I came across Webiny, a hosted platform which recently pivoted to become a self-hosted product, and which has the interesting selling proposition of being a &quot;serverless&quot; CMS.&lt;/p&gt;
&lt;h2&gt;Why I think serverless matters &lt;a href=&quot;https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/#why-i-think-serverless-matters&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s a lot of buzz around the word &quot;serverless&quot; in my world recently, and for good reasons. Although some are keen to point out that the term doesn&apos;t technically mean you&apos;re not using servers, it does have the strong advantage that you only pay for what you use, and if you don&apos;t exceed often generous free initial offerings, you don&apos;t pay for the product ... at all.&lt;/p&gt;
&lt;p&gt;This is how Heroku got so popular. It can afford to give you the space to create an app, knowing that past a certain point it can be destroyed, saving on computing expenses. Your app can then be spun up at some point in the future when called upon at the cost of a minute of two of time.&lt;/p&gt;
&lt;p&gt;Companies today spend a huge amount of money running servers constantly, even when there&apos;s nobody using their services. Imagine if you didn&apos;t have to pay for that downtime?&lt;/p&gt;
&lt;p&gt;That&apos;s why I think serverless is going to become an increasingly large player in the devops space in the next few years.&lt;/p&gt;
&lt;h2&gt;Webiny: truly &quot;serverless&quot;? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/#webiny:-truly-%22serverless%22&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unless your CMS is git-based tool, such as NetlifyCMS, Tina or Forestry, your content needs to be stored on a database somewhere. And therein lies a weakness of any CMS: it depends on writes to one single database, which you could argue doesn&apos;t scale hugely well.&lt;/p&gt;
&lt;p&gt;Webiny is no exception: it has connectors for different databases, and I&apos;ve used Mongo&apos;s hosted service &quot;Atlas&quot; for mine. However does that mean it doesn&apos;t truly fit into the &quot;serverless&quot; paradigm?&lt;/p&gt;
&lt;p&gt;I would argue that it does. And this reveals some of the underlying arguments around the semantics of what &quot;serverless&quot; means. To some people, the term only refers to lambda functions, and not to other things like the authentication service, or the file storage system.&lt;/p&gt;
&lt;p&gt;However, for me, &quot;serverless&quot; means a disparate collection of interrelated services, tied together by common use. By this definition, the file storage system, the authentication service, the database, and everything else, constitutes a serverless application ... i mean, tool ... I mean, whatever.&lt;/p&gt;
&lt;h2&gt;How Webiny helped me learn about serverless &lt;a href=&quot;https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/#how-webiny-helped-me-learn-about-serverless&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Although you can create serverless applications using the online interfaces given to you by different providers, it&apos;s real strength is in allowing you to programmatically create your services as you go.&lt;/p&gt;
&lt;p&gt;This is incredibly powerful. Here&apos;s my Webiny application code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: webiny-apps-xxxxxxx

vars:
  region: ${env.AWS_REGION}

site:
  component: &quot;@webiny/serverless-app&quot;
  inputs:
    description: Webiny Site
    region: ${vars.region}
    memory: 128
    timeout: 30
    code: ./site/build
    env:
      SSR_FUNCTION: ${ssr.name}

ssr:
  component: &quot;@webiny/serverless-function&quot;
  inputs:
    description: Site SSR
    region: ${vars.region}
    hook: yarn build:${cli.env}
    root: ./site
    code: ./site/build-ssr
    handler: handler.handler
    memory: 2048
    timeout: 30

admin:
  component: &quot;@webiny/serverless-app&quot;
  inputs:
    region: ${vars.region}
    description: Webiny Admin
    hook: yarn build:${cli.env}
    root: ./admin

api:
  component: &quot;@webiny/serverless-api-gateway&quot;
  inputs:
    name: Apps Gateway
    binaryMediaTypes: [&quot;*/*&quot;]
    description: Serverless React Apps
    endpoints:
      - path: /admin/{key+}
        method: GET
        function: ${admin}
      - path: /admin
        method: GET
        function: ${admin}
      - path: /{key+}
        method: GET
        function: ${site}
      - path: /
        method: GET
        function: ${site}

cdn:
  component: &quot;@webiny/serverless-aws-cloudfront&quot;
  inputs:
    origins:
      - url: ${api.url}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m not going to break down everything, but you might be able to recognise different services for &quot;site&quot;, &quot;ssr&quot;, &quot;admin&quot;, &quot;api&quot; and &quot;cdn&quot;, etc, which are variously the API gateway, the admin interface, the frontend static site, and some lambda functions.&lt;/p&gt;
&lt;p&gt;They all tie together to make the backend interface work, and to compile a static site hosted on S3.&lt;/p&gt;
&lt;p&gt;And if I log into my AWS dashboard, I can see these services there too ... I mention that just because I have a visual kind of brain.&lt;/p&gt;
&lt;p&gt;This idea, of &quot;infrastructure as code&quot; means your applications are truly portable: you can destroy it and re-create it from its blueprint using the code you&apos;ve written.&lt;/p&gt;
&lt;p&gt;And with the amount I use my Webiny CMS, I&apos;m probably not going to ever pay a thing for it.&lt;/p&gt;
&lt;h2&gt;Try it out! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/learning-serverless-with-webiny/#try-it-out!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I highly recommend giving Webiny a spin. The product is in early stages but is already quite promising. It&apos;s nice that as JavaScript developers, we have a good range of choice between this, the rising star Strapi, and the very mature Ghost.&lt;/p&gt;
&lt;p&gt;What do you think of it? Let me know!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Learning serverless with Webiny&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Let Browsers be Browsers</title><link>https://deliciousreverie.co.uk/posts/let-browsers-be-browsers/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/let-browsers-be-browsers/</guid><description>I&apos;ve been hearing a lot of different, seemingly contradictory messages from the industry in recent weeks regarding frontend frameworks and progressive enhancement. I got so frustrated that I started a discussion on Web Freelancers Hub about it. I felt the answers that came back were worth documenting. </description><pubDate>Mon, 23 Nov 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve been hearing a lot of different, seemingly contradictory messages from the industry in recent weeks regarding frontend frameworks and progressive enhancement. I got so frustrated that I started a discussion on Web Freelancers Hub about it. I felt the answers that came back were worth documenting.&lt;/p&gt;
&lt;p&gt;My original post to the group:&lt;/p&gt;
&lt;p&gt;So, I&apos;m really confused.&lt;/p&gt;
&lt;p&gt;There&apos;s been a huge demand for people who can work with Frontend frameworks especially Angular.js in recent months. For a while, it seemed like I was turning down 5 contracts a week that required this skill.. which I&apos;m now learning.&lt;/p&gt;
&lt;p&gt;But now the industry seems to be eating is tail &amp;amp; saying this is bad practice because what if users don&apos;t have Javascript enabled on their browser (for whatever reason)... and I agree with them.&lt;/p&gt;
&lt;p&gt;I&apos;m a bit confused / frustrated with these mixed messages — I really agree with recent posts from &lt;a href=&quot;https://adactio.com/journal/9963&quot;&gt;Jeremy Keith&lt;/a&gt; and &lt;a href=&quot;https://alistapart.com/article/interaction-is-an-enhancement&quot;&gt;Aaron Gustaffson&lt;/a&gt; but don&apos;t know the best route to take to&lt;/p&gt;
&lt;p&gt;a) stay ahead of the game&lt;/p&gt;
&lt;p&gt;b) provide the best experience for users.&lt;/p&gt;
&lt;p&gt;Anyone else thinking this / have a take on it?&lt;/p&gt;
&lt;p&gt;Although there were a few developers who argued that when developing apps, you have to stipulate to users that JS must be enabled, Nick Bramwell was quick to comment in a way that resonated with me: &quot;I’ve been burnt too many times in the past learning things that quickly vanish.&quot;&lt;/p&gt;
&lt;p&gt;This is one thing that I&apos;ve been afraid of, since it takes a lot of time to learn frameworks ... and I have a young baby to take care of!&lt;/p&gt;
&lt;p&gt;Rachel Andrew mentioned a use case that I don&apos;t think we consider enough: &quot;I travel a lot, end up on terrible wifi. At least once per trip I find myself unable to use a site because bits of their JavaScript haven&apos;t made it.&quot; This is also what motivated &lt;a href=&quot;https://abookapart.com/products/responsible-responsive-design&quot;&gt;Scott Jehl to write his book &quot;Responsible Responsive Design&quot;&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Browsers Are Browsers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/let-browsers-be-browsers/#browsers-are-browsers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So I guess my position is that there may be certain cases we would justifiably use fronted frameworks to enhance the experience for the user, we shouldn&apos;t make it a requirement of our site. Browsers are meant to be used to view and interact with data, and largely cannot be &apos;exploited&apos; — partly because users need protection from such things.&lt;/p&gt;
&lt;p&gt;I guess that if responsive design has taught me one thing, it&apos;s that we can&apos;t know the conditions under which our site will be served up to someone, and that we shouldn&apos;t try to know, or to stipulate conditions unless there is a very good reason for doing so.&lt;/p&gt;
&lt;p&gt;I&apos;m grateful to be part of a community that I can go to when issues like this come up. For now, I&apos;m going to stick with what I&apos;ve learned with regard to fronted frameworks. Perhaps I might start learning server-side (universal, or isomorphic) JavaScript, because I seem to enjoy seeing what&apos;s possible with the language.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Let Browsers be Browsers&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Let&apos;s build a carousel!</title><link>https://deliciousreverie.co.uk/posts/lets-build-a-carousel/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/lets-build-a-carousel/</guid><description>Carousels are a staple of web development life. And thankfully, there are a lot of good off-the-shelf ones ready to build into your project. But it&apos;s a good challenge to see if you can build one yourself.</description><pubDate>Sat, 13 May 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Carousels are a staple of web development life. And thankfully, there are a lot of good off-the-shelf ones ready to build into your project. But it&apos;s a good challenge to see if you can build one yourself.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is somewhat of a personal milestone for me: I remember when I was a new software engineer tinkering with different solutions thinking that I probably would never be able to build one from scratch. But, here it is, I did it in a couple of days.&lt;/p&gt;
&lt;p&gt;This helped me to see that I&apos;m still making progress as an engineer and learning new things.&lt;/p&gt;
&lt;p&gt;But maybe you just want to build a carousel ... if so, we first need to carefully consider whether a carousel is the best option for what we need to display. Carousels are very often not the solution you want: &lt;a href=&quot;https://shouldiuseacarousel.com/&quot;&gt;please check out this site for some statistics&lt;/a&gt;. If you have decided a carousel is best for your UI, then read on.&lt;/p&gt;
&lt;h2&gt;HTML setup&lt;/h2&gt;
&lt;p&gt;I&apos;ll start with explaining the HTML layout here. I&apos;m using Tailwind because that is what I was using at the time, but you can use whatever CSS you like.&lt;/p&gt;
&lt;p&gt;First, we have an opening tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&quot;animation-carousel&quot; class=&quot;tw-relative tw-w-full tw-h-36 md:tw-h-96 tw-my-8&quot; data-carousel&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things of note: We&apos;re going to use a lot of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Positioning#relative_positioning&quot;&gt;relative&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Positioning#absolute_positioning&quot;&gt;absolute&lt;/a&gt; positioning here. To make sure that is scoped to this container, we&apos;re making sure the position is relative. And because each item is going to be positioned in this way, we&apos;re setting a height too.&lt;/p&gt;
&lt;p&gt;Lastly, we&apos;re setting a data attribute. This is what we&apos;re going to pick up in the JavaScript. I could just as easily have used a class name.&lt;/p&gt;
&lt;p&gt;Next, I&apos;ve got the inner wrapper. This will contain the images, previous and next buttons, and indicators (the little navigation pips you sometimes see).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;div class=&quot;tw-relative tw-overflow-hidden tw-rounded-lg tw-h-36 md:tw-h-96&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Indicators&lt;/h3&gt;
&lt;p&gt;Indicators are optional in this setup. If they&apos;re not in the DOM, we won&apos;t render them. To facilitate that, I&apos;ve used a template tag so they don&apos;t render initially, rather, we can clone this template&apos;s inner HTML as many times as we need to render the pips:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      &amp;lt;div id=&quot;indicator-container&quot; class=&quot;tw-absolute tw-z-30 tw-flex tw-space-x-3 tw--translate-x-1/2 tw-bottom-5 tw-left-1/2&quot;&amp;gt;
        &amp;lt;template id=&quot;carousel-indicator&quot;&amp;gt;
          &amp;lt;button type=&quot;button&quot; class=&quot;tw-w-3 tw-h-3 tw-rounded-full tw-border tw-border-white&quot; aria-current=&quot;false&quot; aria-label=&quot;Slide 1&quot; data-carousel-slide-to=&quot;0&quot;&amp;gt;
            &amp;lt;svg viewBox=&quot;0 0 100 100&quot; class=&quot;tw-text-white&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;
              &amp;lt;circle cx=&quot;50&quot; cy=&quot;50&quot; r=&quot;50&quot; fill=&quot;currentColor&quot; stroke=&quot;white&quot; stroke-width=&quot;3&quot;/&amp;gt;
            &amp;lt;/svg&amp;gt;
          &amp;lt;/button&amp;gt;
        &amp;lt;/template&amp;gt;
      &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Template tags aren&apos;t ordinarily rendered by the DOM tree but we can still select them with JavaScript. This is a handy feature as you&apos;ll see later. We&apos;ve got a button with an SVG circle in there so we can click to select a certain slide.&lt;/p&gt;
&lt;p&gt;Each of these has an aria label to indicate which item is focused on. I could have used a visually hidden text label.&lt;/p&gt;
&lt;p&gt;Next come the items themselves.&lt;/p&gt;
&lt;h3&gt;Slides&lt;/h3&gt;
&lt;p&gt;The slides have an opacity of zero to start with - except the  item we wish to set as initially active. We do this so that if there&apos;s a problem with the JavaScript loading, the user will still see one image and not just an empty part of the screen:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      &amp;lt;div class=&quot;tw-opacity-0 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96&quot; data-carousel-item=&quot;active&quot;&amp;gt;
          &amp;lt;img src=&quot;https://placehold.co/600x400/orange/blue&quot; alt=&quot;...&quot;  class=&quot;tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96&quot;&amp;gt;
          &amp;lt;div class=&quot;tw-absolute tw-inset-0 tw-flex&quot;&amp;gt;
            &amp;lt;blockquote class=&quot;tw-m-auto tw-z-40 tw-font-bold tw-text-4xl tw-max-w-xl tw-text-center tw-text-zinc-50&quot;&amp;gt;
                The
                &amp;lt;span class=&quot;tw-text-primary-400&quot;&amp;gt;more I learn&amp;lt;/span&amp;gt;, the more I realise how much I don&apos;t know
                &amp;lt;p class=&quot;tw-mt-6 tw-text-sm tw-font-normal tw-italic&quot;&amp;gt;
                  Albert Einstein
                &amp;lt;/p&amp;gt;
            &amp;lt;/blockquote&amp;gt;
          &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class=&quot;tw-opacity-100 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96&quot; data-carousel-item=&quot;active&quot;&amp;gt;
          &amp;lt;img src=&quot;https://placehold.co/600x400/orange/white&quot; alt=&quot;...&quot; class=&quot;tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div class=&quot;tw-opacity-0 tw-transition tw-duration-150 tw-ease-in-out tw-h-36 md:tw-h-96&quot; data-carousel-item&amp;gt;
          &amp;lt;img src=&quot;https://placehold.co/600x400/red/white&quot; alt=&quot;...&quot; class=&quot;tw-absolute tw-block tw-w-full tw--translate-x-1/2 tw--translate-y-1/2 tw-top-1/2 tw-left-1/2 tw-h-36 md:tw-h-96&quot;&amp;gt;
      &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you&apos;ll notice, as well as images, each slide can contain a text caption too. I could&apos;ve used a figure and figcaption element pair for this, but in my case, the image is purely decorative so I opted for a blockquote for the quotation.&lt;/p&gt;
&lt;p&gt;We&apos;re going to be using the data-carousel-item attribute again in the JavaScript, and attach a value of active to the current slide.&lt;/p&gt;
&lt;h3&gt;Previous &amp;amp; Next Controls&lt;/h3&gt;
&lt;p&gt;These controls render to the left and right of the slider and allow users to cycle through the images in order or reverse order. They have icons indicating the direction they&apos;re cycling in when they&apos;re clicked and a visually hidden label to aid assistive technology users.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;button type=&quot;button&quot; class=&quot;tw-absolute tw-top-0 tw-left-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none&quot; data-carousel-prev&amp;gt;
      &amp;lt;span class=&quot;tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 tw-bg-white/30 tw-dark:bg-gray-800/30 group-hover:tw-bg-white/50 dark:group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none tw-transition-all tw-ease-in-out&quot;&amp;gt;
          &amp;lt;svg aria-hidden=&quot;true&quot; class=&quot;tw-w-5 tw-h-5 tw-text-zinc-400 hover:tw-text-zinc-800 sm:tw-w-6 sm:tw-h-6 tw-dark:text-gray-800 tw-transition-all tw-ease-in-out&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;&amp;lt;path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M15 19l-7-7 7-7&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;
          &amp;lt;span class=&quot;tw-sr-only&quot;&amp;gt;Previous&amp;lt;/span&amp;gt;
      &amp;lt;/span&amp;gt;
  &amp;lt;/button&amp;gt;
  &amp;lt;button type=&quot;button&quot; class=&quot;tw-absolute tw-top-0 tw-right-0 tw-z-30 tw-flex tw-items-center tw-justify-center tw-h-full tw-px-4 tw-cursor-pointer tw-group focus:tw-outline-none&quot; data-carousel-next&amp;gt;
      &amp;lt;span class=&quot;tw-inline-flex tw-items-center tw-justify-center tw-w-8 tw-h-8 tw-rounded-full sm:tw-w-10 sm:tw-h-10 bg-white/30 dark:tw-bg-gray-800/30 group-hover:tw-bg-white/50 dark:tw-group-hover:tw-bg-gray-800/60 group-focus:tw-ring-4 group-focus:tw-ring-white dark:tw-group-focus:tw-ring-gray-800/70 group-focus:tw-outline-none tw-transition-all tw-ease-in-out&quot;&amp;gt;
          &amp;lt;svg aria-hidden=&quot;true&quot; class=&quot;tw-w-5 tw-h-5 tw-text-zinc-400 hover:tw-text-zinc-800 sm:tw-w-6 sm:tw-h-6 dark:tw-text-gray-800 tw-transition-all tw-ease-in-out&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; viewBox=&quot;0 0 24 24&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;&amp;lt;path stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;2&quot; d=&quot;M9 5l7 7-7 7&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;
          &amp;lt;span class=&quot;tw-sr-only&quot;&amp;gt;Next&amp;lt;/span&amp;gt;
      &amp;lt;/span&amp;gt;
  &amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s the HTML, now the fun part.&lt;/p&gt;
&lt;h2&gt;JavaScript&lt;/h2&gt;
&lt;p&gt;Firstly, we have to ensure that the DOM has fully loaded before trying to select our elements, so we wrap the function like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;window.addEventListener(&quot;DOMContentLoaded&quot;, () =&amp;gt; {
  // our code goes here
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event&quot;&gt;More about this here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Next, let&apos;s define our createCarousel function, and just return early in case of some easy to detect errors, for example, if there isn&apos;t a window object, or the selector (the HTML element we want to create a carousel with) hasn&apos;t been passed to the function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function createCarousel(selector) {
  if (typeof window === &apos;undefined&apos;) {
    return;
  }
  if(!selector) {
    console.error(&quot;cannot find element to create a carousel into&quot;);
    return;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let&apos;s pick up some HTML elements relative to the selector that we&apos;re going to need shortly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  const carouselItems = selector.querySelectorAll(&apos;[data-carousel-item]&apos;);
  const carouselItemsArray = Array.from(carouselItems);
  const indicatorTemplate = selector.querySelector(&apos;#carousel-indicator&apos;);
  // define interval timer so we can clear it later
  let intervalInstance = null;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this bit of code we&apos;re getting the slides themselves. But using querySelector() returns a NodeList collection instead of an Array, so I&apos;m making it into an Array that&apos;ll be handy later on. We&apos;re also getting the indicator template, and also setting an Interval, which is a timer we use to know how long we should wait before scrolling to the next slide. I want to define it here but set it as null so we can assign it and reference it in other functions.&lt;/p&gt;
&lt;h2&gt;Helper functions&lt;/h2&gt;
&lt;p&gt;Now for some helper functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function getActiveItem() {
    return selector.querySelector(&apos;[data-carousel-item=&quot;active&quot;]&apos;);
  }
  function getPositionOfItem(item) {
    return carouselItemsArray.findIndex((carouselItem) =&amp;gt; {
        return carouselItem === item;
    });
  }
  function setItemAsActive(item) {
    item.setAttribute(&apos;data-carousel-item&apos;, &apos;active&apos;);
    item.classList.remove(&apos;tw-opacity-0&apos;);
    item.classList.add(&apos;tw-opacity-100&apos;);

    // update the indicators if available
    const currentItemIndex = getPositionOfItem(item);
    const indicators = selector.querySelectorAll(&apos;[data-carousel-indicator]&apos;);
    indicators.length &amp;gt; 0 &amp;amp;&amp;amp; Array.from(indicators).map((indicator, index) =&amp;gt; {
      if (index === currentItemIndex) {
        indicator.setAttribute(&apos;aria-current&apos;, &apos;true&apos;);
        indicator.querySelector(&apos;svg&apos;).classList.add(&apos;tw-text-primary-600&apos;);
        indicator.querySelector(&apos;svg&apos;).classList.remove(&apos;tw-text-white&apos;);
      } else {
        indicator.querySelector(&apos;svg&apos;).classList.add(&apos;tw-text-white&apos;);
        indicator.setAttribute(&apos;aria-current&apos;, &apos;false&apos;);
        indicator.querySelector(&apos;svg&apos;).classList.remove(&apos;tw-text-primary-600&apos;);
        indicator.querySelector(&apos;svg&apos;).classList.add(&apos;tw-text-white&apos;);
      }
    });
  }
  function setItemAsInactive(item) {
    item.setAttribute(&apos;data-carousel-item&apos;, &apos;&apos;);
    item.classList.add(&apos;tw-opacity-0&apos;);
    item.classList.remove(&apos;tw-opacity-100&apos;);
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. getActiveItem() (no parameters)&lt;/h3&gt;
&lt;p&gt;At several points, we&apos;re going to need to get the currently active item. And since that changes frequently, I don&apos;t want to store it in a variable in the main function&apos;s scope since that will mean it will only be called once and hold the same value. Instead I have made a small function for this so I could more easily determine what the intent of this selector is.&lt;/p&gt;
&lt;h3&gt;2. getPositionOfItem(item)&lt;/h3&gt;
&lt;p&gt;As it says, this one finds the position of a slide as an index of the array we created earlier.&lt;/p&gt;
&lt;h3&gt;3. setItemAsActive(item)&lt;/h3&gt;
&lt;p&gt;This one is a bit more complicated but it&apos;s name should inform you of what it does. As well as making an item active, it also updates the indicators (if they exist) to identify the index of the slide.&lt;/p&gt;
&lt;p&gt;This was tricky because you have to match the index of a slide with the correct index of the indicators, and you have to remove the active classes from other indicators too.&lt;/p&gt;
&lt;h3&gt;4. setItemAsInactive(item)&lt;/h3&gt;
&lt;p&gt;I could possibly have done this in the function above, but that would combine two intents into one function, which would make it harder to understand. It would also mean adding an extra parameter which complicates things somewhat. This way, both functions that set items active and inactive and, if something else needs to happen when slides change, they&apos;re easily identifiable and manageable.&lt;/p&gt;
&lt;p&gt;That&apos;s it for the helpers, now I want to set up some actions that the carousel will perform:&lt;/p&gt;
&lt;h2&gt;Actions&lt;/h2&gt;
&lt;p&gt;Actions define either responses to user interactions or things that happen at a set interval&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function cycle() {
    intervalInstance = window.setInterval(() =&amp;gt; {
        next();
    }, 3_000);
  }
  function pause() {
      clearInterval(intervalInstance);
  }
  function slideTo(nextItem) {
    const activeItem = getActiveItem();
    setItemAsInactive(activeItem);
    setItemAsActive(nextItem);
    pause();
    cycle();
  }
  function next() {
    let nextItem = null;
    const activeItem = getActiveItem();
    const activeItemPosition = getPositionOfItem(activeItem);
    if (activeItemPosition === carouselItems.length - 1) {
        // if it is the last item, set first item as next
        nextItem = carouselItems[0];
    } else {
        nextItem = carouselItems[activeItemPosition + 1];
    }
    slideTo(nextItem);
  }
  function prev() {
    let prevItem = null;
    const activeItem = getActiveItem();
    const activeItemPosition = getPositionOfItem(activeItem);

    if (activeItemPosition === 0) {
        prevItem = carouselItems[carouselItems.length -1];
    } else {
        prevItem = carouselItems[activeItemPosition - 1];
    }
    slideTo(prevItem);
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. cycle() (no parameters)&lt;/h3&gt;
&lt;p&gt;Cycle sets up the interval that we defined at the start of the createCarousel function to 3 seconds and then shows the next carousel item.&lt;/p&gt;
&lt;h3&gt;2. pause()&lt;/h3&gt;
&lt;p&gt;Pause clears the interval so when the next slide shows we start again from 3 seconds instead of whatever was remaining in the timer. I named it pause because I thought I might allow external access to the function so that the slider can be paused. In retrospect, it doesn&apos;t do that yet so I should have changed the name to reflect what it does.&lt;/p&gt;
&lt;h3&gt;3. slideTo(nextItem)&lt;/h3&gt;
&lt;p&gt;Because this function takes the next item we want to set as active as a parameter, we can call this function any time the user clicks on a prev or next button, or the indicators.  It uses the two helper functions to change the active item, resets the interval and then cycles the carousel.&lt;/p&gt;
&lt;h3&gt;4. next() (no parameters)&lt;/h3&gt;
&lt;p&gt;There&apos;s a little trick to this one which means we have to find out if we&apos;re on the last item, and if so, the next slide to show should be the first item. See how converting the NodeList into an Array came in handy?&lt;/p&gt;
&lt;h3&gt;5. prev() (no parameters)&lt;/h3&gt;
&lt;p&gt;This is functionally very similar to the next() function except it&apos;s only called by someone hitting the &quot;previous&quot; button.&lt;/p&gt;
&lt;h2&gt;Initialize carousel&lt;/h2&gt;
&lt;p&gt;I could have put this segment of code into the main closure of the createCarousel() function, but I think having it as a separate body makes it easier to see the intent of what we&apos;re doing and it&apos;s also clearer to read.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function init() {
    const activeItem = getActiveItem();

    const items = Array.from(carouselItems)
    items.map(item =&amp;gt; {
      item.classList.add(
        &apos;tw-absolute&apos;,
        &apos;tw-inset-0&apos;,
        &apos;tw-transition-transform&apos;,
        &apos;tw-transform&apos;
      )
    });
    /**
     * if no active item is set then first position is default
    */
    if(activeItem) {
      slideTo(activeItem);
    } else {
      slideTo(0)
    }
    /**
     * Add event listeners to the buttons if they exist
     */
    const nextButton = selector.querySelector(&apos;[data-carousel-next]&apos;);
    nextButton &amp;amp;&amp;amp; nextButton.addEventListener(&apos;click&apos;, () =&amp;gt; {
      next();
    });
    const prevButton = selector.querySelector(&apos;[data-carousel-prev]&apos;);
    prevButton &amp;amp;&amp;amp; prevButton.addEventListener(&apos;click&apos;, () =&amp;gt; {
      prev();
    });
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we&apos;re leveraging the small helper and action functions we defined above to set some classes on each of the items, making sure the user sees the active item. This also covers the case that the user assigns a slide other than the first slide as the one that should initially be active. It then adds event listeners to the next and previous buttons if they exist in the DOM.&lt;/p&gt;
&lt;p&gt;Lastly in the main body of the createCarousel() function we call these functions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  init();
  // if we have an indicator template, create the indicators
  indicatorTemplate &amp;amp;&amp;amp; createIndicators();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like I said, this last step could&apos;ve been avoided if I&apos;d put the body of init() and createIndicators() functions in the main function, I merely thought this was better from an organisational point of view.&lt;/p&gt;
&lt;h2&gt;Create all of the Carousels&lt;/h2&gt;
&lt;p&gt;Here&apos;s the final step, which we&apos;ll do outside of our createCarousel() function but still inside the DOMContentLoaded listener:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const allCarousels = document.querySelectorAll(&apos;[data-carousel]&apos;)
allCarousels.forEach((carouselElement) =&amp;gt; {
    createCarousel(carouselElement);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which is to find all of the DOM elements with data-carousel attributes and create a carousel for them. Now each carousel will have it&apos;s own indicators, event listeners and intervals which run independently of each other.&lt;/p&gt;
&lt;p&gt;And also, if there are no elements found, the createCarousel function won&apos;t be called at all.&lt;/p&gt;
&lt;h2&gt;Further Improvements&lt;/h2&gt;
&lt;p&gt;Now that the basic carousel functionality is complete, what could I do to further improve the code?&lt;/p&gt;
&lt;p&gt;I&apos;ve seen a lot that I can do.&lt;/p&gt;
&lt;p&gt;For example, there&apos;s no API. I would like to allow access to some things such as the interval timer so that the speed with which carousel items cycle can be defined on a per-use basis. I&apos;d also like to allow access to other things such as the prev and next buttons. Perhaps analytics need to be added to these listeners in some cases.  Or perhaps someone will need to pause the carousel programatically for some reason.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating this carousel wasn&apos;t just a technical challenge. It was also a communciation challenge: how do I build this in a way that is easy to understand for those needing to modify my code? Does it explain itself easily? Does it allow another engineer to focus on their task and not ask too many mental gymnastics of them?&lt;/p&gt;
&lt;p&gt;This is what we must aim for when we&apos;re writing modules like this; we must be first and foremost kind to both other engineers who will come along later, and our future selves.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://gist.github.com/endymion1818/8119f7af21db1f62d9119581fc3a8d19&quot;&gt;Full code here.&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Let&apos;s build a carousel!&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Lowest tech first</title><link>https://deliciousreverie.co.uk/posts/lowest-tech-first/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/lowest-tech-first/</guid><description>Should I use CSS or JavaScript to perform this task? If you&apos;re asking yourself this question, I have an answer for you: use the lowest technology available to you which can complete the task. </description><pubDate>Wed, 23 Jun 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Should I use CSS or JavaScript to perform this task? If you&apos;re asking yourself this question, I have an answer for you: use the lowest technology available to you which can complete the task.&lt;/p&gt;
&lt;p&gt;I did a lot of prop threading the other day. I was updating our navigation section so that the last dropdown in the navigation would be positioned better.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;NavList isLastItem={index.length - 1 ? true : false} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My colleague pointed out that I was threading this through 3 successive components so I could apply some CSS to the menu, which is OK sometimes, but there could be a better way of doing it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;:last-of-type {  left: 0;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why is this better?&lt;/p&gt;
&lt;p&gt;Because it&apos;s using the lowest tech first principle. I&apos;m setting the property in CSS which means there&apos;s less overhead for the browser to maintain, less prop threading going on, and it&apos;s arguably easier to see what&apos;s going on in the styles of this component.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Lowest tech first&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Losing Peter</title><link>https://deliciousreverie.co.uk/posts/losing-peter/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/losing-peter/</guid><description>I found out one of my closest school friends committed suicide recently.</description><pubDate>Thu, 14 Sep 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I don&apos;t recall how Peter and I became friends. We must&apos;ve struck up some conversation at school, is the only thing I can think of, and quickly found out we had similar interests. Peter is the guy who I used to call up on a Tuesday evening after the latest episode of Babylon 5 aired, and talk abut the episode, speculate about further plot details, and share our amazement of its then cutting edge FX.&lt;/p&gt;
&lt;p&gt;Peter was adopted. He never talked about this situation apart from on one occasion where we were pranking each other that we were both secretly aliens. Of course, being adopted was great backstory material.&lt;/p&gt;
&lt;p&gt;As time went on, Peter and I got more intensely interested in writing. We used to meet and flush out story outlines for hours, discussing main plot points, charachters, cultures, technology (most of them were science fiction stories, of course) and story arcs. Peter had a natural talent and I enjoyed listening to him flesh out an idea from nothing.&lt;/p&gt;
&lt;p&gt;Quite often our imaginations would be fired by some news of this new technology called The Internet. I had been given a 14.4k Apple modem and would excitedly chime &quot;Let&apos;s try to get on the internet!&quot;. Hours passed, and we would still be waiting for a beta of NCSA Mosaic or later Netscape Navigator to download, and feel frustrated when all we got was a blue box where a gif header image should be.&lt;/p&gt;
&lt;p&gt;14.4k was becoming slow even by standards of those days.&lt;/p&gt;
&lt;h2&gt;Drifting Apart &lt;a href=&quot;https://deliciousreverie.co.uk/posts/losing-peter/#drifting-apart&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Peter and I didn&apos;t stay friends for long after school. We went to different colleges (and were in different year groups - he was a year older than me). We did meet up a few times. He&apos;d gotten into the madcap humour of Monty Python&apos;s Flying Circus. This kind of humour was beyond me and I didn&apos;t understand why he was rolling around, creased double on the floor at the absurdity of it until much later on in life.&lt;/p&gt;
&lt;h2&gt;Final Scenes &lt;a href=&quot;https://deliciousreverie.co.uk/posts/losing-peter/#final-scenes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I don&apos;t know how we came to meet the last time. Perhaps we saw one another in the stret whilst Peter was at Cardiff Uni, and I was working in a recruitment office nearby.&lt;/p&gt;
&lt;p&gt;By that point we had not seen each other in a year or two. Peter seemed distracted. There was no absurd laughter, no jollility, and scarcely a mention of the things we used to enjoy.&lt;/p&gt;
&lt;p&gt;Looking back, I have to wonder if this wasn&apos;t a silent plea for help. He clearly wasn&apos;t himself. I think now, perhaps that wasn&apos;t due to his occupation with his studies, his involvement with some relationship or whatever else.&lt;/p&gt;
&lt;p&gt;Instead, was it an absence of anything that he might have otherwise been occupied with. I know from personal forays into feelings of depression, it&apos;s the absence of anything rather than a struggle to find common ground that results in these unhealthy silences.&lt;/p&gt;
&lt;p&gt;&quot;As you know, we don&apos;t have Peter now&quot;, his adoptive mother said to my mother when she met her shopping. Again, a shielding, or an absence of emotion, similar to Peters, I thought.&lt;/p&gt;
&lt;h2&gt;Final Thoughts &lt;a href=&quot;https://deliciousreverie.co.uk/posts/losing-peter/#final-thoughts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Peter&apos;s gone. But I wonder regularly, if not daily, if I could have done anything to change his mind. I didn&apos;t speak to him about my faith after he expressed contentment with his Catholic heritage. Would that have made any difference? I didn&apos;t breach many personal subjects with him. I didn&apos;t even keep in touch. Was that neglectful of me?&lt;/p&gt;
&lt;p&gt;I wsh I could talk to him again, if only just to ask him, &quot;Why did you choose that path?&quot;, to try to understand what it was that made him take his life.&lt;/p&gt;
&lt;p&gt;My father thinks that individuals tend to be more compassionate after a bereavement. That the harrowing experience of losing someone makes you see our temporary situation for what it is, and are more appreciative of friendships and relationships we have because of that.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Losing Peter&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Managing Quality across Different Codebases</title><link>https://deliciousreverie.co.uk/posts/managing-quality-across-different-codebases/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/managing-quality-across-different-codebases/</guid><description>Having a structured plan for the codebases in your project will help keep them in good shape. I&apos;ve written this document as a guideline of sorts, feel free to adopt it, adapt it or use it as a basis your own.</description><pubDate>Tue, 05 Dec 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;One of my roles as the sole JavaScript Engineer is that of planning for the future. I hope that the company will grow to the point where other JavaScript engineers will be able to join. But even if it doesn&apos;t, I have outlined a definitive plan to continuously improve the code here.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Having a structured plan for the codebases in your project will help extend their lifecycles. It will also help those using the code to more easily manipulate them to fulfil new business requirements whatever these might be.&lt;/p&gt;
&lt;p&gt;To that end, I wrote this document as a guideline for my company, feel free to adopt, adapt it or use it as a basis your own.&lt;/p&gt;
&lt;h2&gt;Index&lt;/h2&gt;
&lt;p&gt;Lists the sections in this document, hyperlinking them for easy navigation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Code quality&lt;/li&gt;
&lt;li&gt;Type Safety&lt;/li&gt;
&lt;li&gt;Unit Tests&lt;/li&gt;
&lt;li&gt;End to end Testing&lt;/li&gt;
&lt;li&gt;Deployment mechanisms&lt;/li&gt;
&lt;li&gt;Error reporting&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Standardisation&lt;/li&gt;
&lt;li&gt;Scalability&lt;/li&gt;
&lt;li&gt;Education&lt;/li&gt;
&lt;li&gt;Code safety&lt;/li&gt;
&lt;li&gt;Modularisation&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Appendix 1: Background to Projects&lt;/li&gt;
&lt;li&gt;Appendix 2: How we write JavaScript Code&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Rationale&lt;/h2&gt;
&lt;p&gt;I have tried to answer the question &quot;Why does this document exist?&quot; and clarified why this is a helpful document to maintain.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This should be a living document that helps chart the progress and objectives for JavaScript concerns at [company name].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Outlines the nature of the code in use at the company from a very high level. For my document, this highlights the 3 main different types of JavaScript codebases that exist in the company.&lt;/p&gt;
&lt;h2&gt;Sections&lt;/h2&gt;
&lt;p&gt;For each of the sections mentioned in the index, give a high level overview or history, outline what objectives you have for the future, show the current status of those objectives and explain what has been done so far:&lt;/p&gt;
&lt;h3&gt;Example of Section 1:&lt;/h3&gt;
&lt;blockquote&gt;
&lt;h3&gt;1. Code Quality&lt;/h3&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;All of the JavaScript code repositories are working well and producing the needed outcomes for the business. There are some things that could yet be done to improve and standardise the quality of code to ensure that we are efficiently using developer time and improving deliverability and confidence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;Objectives:&lt;/h4&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Use the same linting rules for all packages where possible&lt;/li&gt;
&lt;li&gt;Use language-specific coding styles (eg. functional over classes)&lt;/li&gt;
&lt;li&gt;Use a single compiler where possible and follow the latest ECMAScript standards&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;Status:&lt;/h4&gt;
&lt;p&gt;Not started / In Progress / Completed / Cancelled&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;h4&gt;Details:&lt;/h4&gt;
&lt;p&gt;We will use Vite as a bundler and transpile from ESM where necessary. Each major concern of JavaScript code will be renovated and receive maintenance when possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Appendices&lt;/h2&gt;
&lt;p&gt;At the end of the sections, I have two appendices: one which has an overview of each codebase, and another that builds on the section on &quot;Standardization of Code&quot;.&lt;/p&gt;
&lt;h3&gt;Example of Appendix 1 (Background to Projects):&lt;/h3&gt;
&lt;blockquote&gt;
&lt;h4&gt;Project name&lt;/h4&gt;
&lt;p&gt;This application provides a service to ...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Repo&lt;/strong&gt;: https://github.com/dummy&lt;br /&gt;
&lt;strong&gt;Hosting&lt;/strong&gt;: https://link.to/hosting-platform-backend&lt;br /&gt;
&lt;strong&gt;Pipeline&lt;/strong&gt;: Github Actions&lt;br /&gt;
&lt;strong&gt;Code quality&lt;/strong&gt;: Good/ Bad / Reasonable&lt;br /&gt;
&lt;strong&gt;Tests&lt;/strong&gt;: None / Some / E2E only / Unit only&lt;br /&gt;
&lt;strong&gt;Status&lt;/strong&gt;: In development / In production / Being renovated / Up to date&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Example of Appendix 2 (How we write JavaScript Code):&lt;/h3&gt;
&lt;p&gt;Many thinks to my friend Josh Farley for this one. It&apos;s a table which hilights decisions on code quality and standardisation that aren&apos;t covered by Prettier or the AirBnB Style Guide.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;overflow-y-scroll&quot;&amp;gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Concern&lt;/td&gt;
&lt;td&gt;Decision&lt;/td&gt;
&lt;td&gt;Rational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File naming&lt;/td&gt;
&lt;td&gt;Files should be snake, eg.Good:&lt;code&gt;detect-provider.js&lt;/code&gt;Bad:&lt;code&gt;detectProvider.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;This makes files easier to scan quickly in the source view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File structure&lt;/td&gt;
&lt;td&gt;Files should not be nested beyond 2 levels.Good:root/src/component.jsBad:root/src/components/utils/frontend/component.js&lt;/td&gt;
&lt;td&gt;This makes it much easier to discover components and avoids duplication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spelling&lt;/td&gt;
&lt;td&gt;Use American english spelling for variables and function names&lt;/td&gt;
&lt;td&gt;Simplifies code for non-native speakers. One standard is better than none.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Managing Quality across Different Codebases&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>monorepos.</title><link>https://deliciousreverie.co.uk/posts/monorepos/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/monorepos/</guid><description>I&apos;ve recently been introduced to the use of monorepos to manage projects and have a few thoughts on the idea. </description><pubDate>Sun, 04 Jul 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve recently been introduced to the use of monorepos to manage projects, and have a few thoughts on the idea.&lt;/p&gt;
&lt;p&gt;First of all, I&apos;d better explain what a monorepo is. The idea is that instead of having many different Git repositories for a group of related projects, you have just one, containing all of your different projects that come under a common umbrella.&lt;/p&gt;
&lt;p&gt;Many projects that I am interested, and to some extent involved in, use monorepo tools to manage their work.&lt;/p&gt;
&lt;h2&gt;Advantages &lt;a href=&quot;https://deliciousreverie.co.uk/posts/monorepos/#advantages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few of the advantages are that you can more easily get a &quot;birds eye&quot; view of the project, so if you&apos;re a new starter you can easily see what the coding styles are in other projects that might be similar to yours. This helped me when I started working at Purple Bricks: I could use some of the code that was already there in other applications to call APIs, build UIs and other things without having to ask my team mates hundreds of questions.&lt;/p&gt;
&lt;p&gt;Another advantage is that you can also share resources such as NPM packages in a more uniform manner. There&apos;s often just one node_modules folder instead of one for each project, and you can have one package.json, meaning every project uses the same version of each module you have installed or built. If you publish a new version of a shared library, the updates are pulled into the project on the next release, which can save a lot of laborious manual updates.&lt;/p&gt;
&lt;h2&gt;Necessities &lt;a href=&quot;https://deliciousreverie.co.uk/posts/monorepos/#necessities&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s a flipside to that though: because shared modules are updated instantaneously, thorough testing is needed to make sure and changes aren&apos;t going to detrimentally affect some projects.&lt;/p&gt;
&lt;p&gt;There&apos;s also quite a bit of configuration involved, since all of your unit, integration and end-to-end tests are typically managed through the monorepo. I&apos;m currently using the &lt;a href=&quot;https://nx.dev/&quot;&gt;Nx monorepo management tool from Nrwl&lt;/a&gt;, which has done a lot to streamline the development process.&lt;/p&gt;
&lt;p&gt;Nx has a command-line tool, so you can run nx affected:test to run tests for all of the applications you&apos;ve affected, or altered, with the code you&apos;re writing. Nrwl claims to be very smart about this, so it can tell what&apos;s been updated and only test those affected apps.&lt;/p&gt;
&lt;p&gt;In reality this means that if you change something in a shared library, you have to run all of the tests for that library, as well as all of the tests for each of the affected applications. This can take quite a considerable amount of time as you add different applications to your workflow, and in my experience it soon gets really slow.&lt;/p&gt;
&lt;p&gt;We have a common shared UI library which is in active development, as most shared UI libraries tend to be. Each time we touch anything to do with these, it takes up to an hour for all of the CI checks to complete, and there can be timeout issues depending on the availability of the pipeline.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lerna.js.org/&quot;&gt;Lerna&lt;/a&gt;, an alternative, older monorepo management tool, has a concept of &lt;a href=&quot;https://github.com/lerna/lerna#independent-mode&quot;&gt;independent projects&lt;/a&gt;, which works similarly to if you have separate repositories. But that does negate some of the streamlining that shared projects have.&lt;/p&gt;
&lt;h2&gt;Verdict &lt;a href=&quot;https://deliciousreverie.co.uk/posts/monorepos/#verdict&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So far, I&apos;ve benefited quite a bit by using Nx to manage our projects. development is somewhat simpler, it&apos;s learner friendly, and streamlines some of the issues you may have experienced if you&apos;ve worked on projects with interdependent code.&lt;/p&gt;
&lt;p&gt;However, it comes at quite a high price: configuration can be a pain, and build times do become extremely frustrating.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:monorepos.&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>More Questions, Better Answers</title><link>https://deliciousreverie.co.uk/posts/more-questions-better-answers/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/more-questions-better-answers/</guid><description>Working with a large FinTech organisation has taught me a lot about the potential loopholes and considerations around certain technical decisions that I have been asked to contribute to.</description><pubDate>Fri, 08 Jun 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Working with a large FinTech organisation has taught me a lot about the potential loopholes and considerations around certain technical decisions that I have been asked to contribute to.&lt;/p&gt;
&lt;p&gt;One of the decisions that we have considered recently is how to deliver content into the new website we are building.&lt;/p&gt;
&lt;p&gt;Historically, this organisation have used CMSes only on the perhipery of the project; that is, the marketing team has a blog tht uses a popular CMS platform, other content is delivered via APIs. But the main site is plain HTML, with content embedded directly into HTML pages managed by a Ruby application.&lt;/p&gt;
&lt;p&gt;Initially, I pushed for a CMS, thinking it would be a great idea. However, I didn&apos;t stop to think about the question more deeply. An experienced technical lead said that a CMS &quot;didn&apos;t solve problems we already have&quot; and that it would introduce unneeded complexity into the project.&lt;/p&gt;
&lt;p&gt;I found this comment very interesting. Particularly because the world I&apos;ve previously been surrounded with has been dominated by the need for a CMS to manage content.&lt;/p&gt;
&lt;p&gt;With my current organisation, a very tech savvy company where about one quarter of the workforce are developers, was there a real need for a CMS?&lt;/p&gt;
&lt;p&gt;Given this comment, I was prompted to think: what problem was I trying to solve with my recommendation?&lt;/p&gt;
&lt;h2&gt;Undo the Answer &lt;a href=&quot;https://deliciousreverie.co.uk/posts/more-questions-better-answers/#undo-the-answer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The charachter 无 (pronounced wú in Chinese or mu in Japanese) means more than just the negative, so writes author Dan Simmons. It&apos;s apparently more like a desire to &quot;undo&quot; what&apos;s already been said, or the absence of the object it refers to.&lt;/p&gt;
&lt;p&gt;I&apos;ve been trying to &quot;undo the answer&quot; in reorganising my mind to think more carefully and understand the questions I come up with.&lt;/p&gt;
&lt;p&gt;In reality, it&apos;s quite easy to propose an answer that might fit. The answer you propose could be the correct one, particularly if you&apos;ve encountered a similar question in the past.&lt;/p&gt;
&lt;p&gt;But often this approach is flawed, and cracks in the process turn quickly to great gulfs which cause frustration and abandonment further along in the project.&lt;/p&gt;
&lt;p&gt;So what was I trying to solve? I ultimately decided it was two: separation of concerns, and with that the absence of a single source of truth.&lt;/p&gt;
&lt;h3&gt;Separation of Concerns &lt;a href=&quot;https://deliciousreverie.co.uk/posts/more-questions-better-answers/#separation-of-concerns&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;By embedding our content in the markup, we were directly mixing content and page structure. This had already become complicated when we were tasked with extracting text from the website so that it could be re-written to match our new tone of voice.&lt;/p&gt;
&lt;p&gt;The agency wanted to be supplied with CSVs containing each page content. I got two thirds of the way into building a content scraper before realising that the markup was so unique, and the CSV so prescriptive, that that solution just wasn&apos;t going to be practical.&lt;/p&gt;
&lt;p&gt;Once again, solutions-based thinking hits a brick wall.&lt;/p&gt;
&lt;h3&gt;Single Source of Truth &lt;a href=&quot;https://deliciousreverie.co.uk/posts/more-questions-better-answers/#single-source-of-truth&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;React excels at maintaining a single source of truth for your code. The uptake of React as a framework has increased because it extols this approach.&lt;/p&gt;
&lt;p&gt;Whereas before you had HTML markup that could be altered by both your server-side code and client-side JavaScript, as well as CSS that could be altered by your JavaScript.&lt;/p&gt;
&lt;p&gt;What React does is bring those 3 elements into JavaScript itself. Now you have a clearer picture of what&apos;s going on because you are only working with one source of truth.&lt;/p&gt;
&lt;p&gt;However, content should stay firmly out of this equation wherever possible. The reason for that is how it&apos;s being manipulated.&lt;/p&gt;
&lt;p&gt;Content authors still have the need to work with the content, even after the deadline for &quot;final copy&quot; has passed. However, it&apos;s likely most of them won&apos;t know HTML, let alone JavaScript, and won&apos;t want to learn. It&apos;s not their job after all.&lt;/p&gt;
&lt;p&gt;To match the design and UX requirements of a project we need to split forms into different screens, present content in different &amp;lt;div&amp;gt;s, and use interactive elements such as tabs, accordions and content sliders.&lt;/p&gt;
&lt;p&gt;We might want to serve different re-useable templates for different pages too.&lt;/p&gt;
&lt;p&gt;My solution of employing a CMS to do enable this was hitting on the idea, however inexpertly. So I framed it differently.&lt;/p&gt;
&lt;p&gt;Now, because I outlined the problems we&apos;re having accuratey, and a solution which doesn&apos;t add complexity (in fact it could reduce it), we&apos;re pursuing this route.&lt;/p&gt;
&lt;h2&gt;Unask the Answer, Not The Question &lt;a href=&quot;https://deliciousreverie.co.uk/posts/more-questions-better-answers/#unask-the-answer-not-the-question&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&quot;Never fear running out of answers, only running out of questions&quot;— J Straczynski&lt;/p&gt;
&lt;p&gt;I&apos;ve decided that asking questions isn&apos;t to be feared or looked down upon, however silly the question may seem at the time. It&apos;s proposing the wrong solution ignorantly that leads to trouble.&lt;/p&gt;
&lt;p&gt;I&apos;m going to make more effort to think about questions more carefully. It&apos;s intent, background, even the thought process that prompted it.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:More Questions, Better Answers&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The Music of Mike Oldfield</title><link>https://deliciousreverie.co.uk/posts/music-of-mike-oldfield/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/music-of-mike-oldfield/</guid><description>I&apos;ve enjoyed the music of Mike Oldfield since I was about 15. Wow, I&apos;ve just realised that&apos;s about 20 years. What makes me keep coming back to this artist? What tracks would I recommend if you wanted to get an overview of his work? </description><pubDate>Sun, 09 Jul 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve enjoyed the music of Mike Oldfield since I was about 15. Wow, I&apos;ve just realised that&apos;s about 20 years. What makes me keep coming back to this artist? What tracks would I recommend if you wanted to get an overview of his work?&lt;/p&gt;
&lt;p&gt;I used to hang out with a group of kids who loved all kinds of music. I admit that I didn&apos;t have much of an idea myself, but took on board their recommendations, and quite early on one of them gave me a cassette tape of &quot;Tubular Bells 2&quot;.&lt;/p&gt;
&lt;p&gt;I had never heard of the artist before, but that wasn&apos;t unusual. What impressed me initially were the titles of some of the tracks. &quot;Weightless&quot;, &quot;Blue Dawn&quot; and &quot;Sentinel&quot; sounded awesome to this hapless, sci-fi loving word-nerd. But when I stuck it in my stereo, it was the sound that really amazed me.&lt;/p&gt;
&lt;p&gt;Looking back now on the albums released around that time, I can see this one represented a clear shift in Mike&apos;s style. For years he&apos;d been releasing albums of rock music, with tracks typically around 5 minutes, lots of prominent vocals by mostly unknown artists, and some outstanding music and guitar solos.&lt;/p&gt;
&lt;p&gt;Over the years, my favourite albums from this era have become &quot;Islands&quot;, &quot;Discovery&quot; and &quot;Crises&quot;, with the latter being the most challenging and rewarding of the three.&lt;/p&gt;
&lt;h2&gt;Paradigm Shift &lt;a href=&quot;https://deliciousreverie.co.uk/posts/music-of-mike-oldfield/#paradigm-shift&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;But this album, Tubular Bells 2, was a paradigm shift. When &quot;Songs of Distant Earth&quot; was released a short time later, I instantly bought the CD (for £18 I might add). I listened to it constantly for years.&lt;/p&gt;
&lt;p&gt;This album still is my absolute favourite. It&apos;s sublime arrangements, for me at least, defy any genre classification. They instill in me a sense of wonder that only the TV series &quot;Babylon 5&quot; has managed to compete with.&lt;/p&gt;
&lt;p&gt;At the time, I was getting into James Joyce. His idea of the &quot;literary epiphany&quot;, a moment when you might percieve some kind of absolute beauty, I think is expressed by this album. Loved it.&lt;/p&gt;
&lt;p&gt;Over the next few years I soaked up everything Mike published, which morphed into a dance / trance influenced style, I am told which came from living on the island of Ibiza.&lt;/p&gt;
&lt;p&gt;I think of this era of ending with the very left-field classical album &quot;Music of the Spheres&quot;. Mike doesn&apos;t talk about this album much, and I secretly wonder if was a studio-ordered collaboration between Mike and Karl Jenkins, which may have been difficult for such an independent and determined artist.&lt;/p&gt;
&lt;p&gt;The album is nonetheless fantastic and one I listen to very frequently.&lt;/p&gt;
&lt;p&gt;Others that I would recommend from this era are &quot;Tubular Bells 3&quot; and &quot;Tr3s Lunas&quot;.&lt;/p&gt;
&lt;p&gt;But there are two albums near the beginning and end of this group that are totally different: &quot;Voyager&quot;, which consists mostly of arrangements of folk music, and &quot;Man on the Rocks&quot;, a heavily vocal-led collection which seems to have been a bit of a catharthis from the solely classical &quot;Music of the Spheres&quot;.&lt;/p&gt;
&lt;h2&gt;Origin Story &lt;a href=&quot;https://deliciousreverie.co.uk/posts/music-of-mike-oldfield/#origin-story&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mike had originally started out bringing, as I like to call it, classical structure to rock music. His original few albums had very few track splits, each being around the 20-minute mark.&lt;/p&gt;
&lt;p&gt;I was very pleased to hear that Mike was going to go back to this style with &quot;Return to Ommadawn&quot;, for a few reasons.&lt;/p&gt;
&lt;p&gt;Primarily, it seems this album was &quot;one for the fans&quot; - Mike was rewarding the people who had stuck with him through the years, even if it seemed as if that style of his was a thing of the past. But listening to interviews, it&apos;s clear that this is Mike&apos;s &quot;home style&quot; - that he&apos;s most comfortable with this structure and form of composition.&lt;/p&gt;
&lt;p&gt;I must be honest, it took me years to understand and appreciate these albums.&lt;/p&gt;
&lt;p&gt;I think my first mistake was believing that I had to enjoy the original Tubular Bells in order to do so. I didn&apos;t - and still don&apos;t - enjoy the one single album that people generally remember Mike for!&lt;/p&gt;
&lt;p&gt;Yes it was a groundbreaking album that displayed a certain genius. But for me, it doesn&apos;t resonate for some reason.&lt;/p&gt;
&lt;p&gt;Now, going through that early set, I genuinely can say I enjoy these albums as much as the others. Something that helped a great deal was the &lt;a&gt;Elements album&lt;/a&gt;, which showcases shortened excerpts from these longer works.&lt;/p&gt;
&lt;h2&gt;The Future &lt;a href=&quot;https://deliciousreverie.co.uk/posts/music-of-mike-oldfield/#the-future&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Mike Oldfield has produced a body of work that I constantly go back to. I eagerly anticipate his next work, and appreciate that even if I don&apos;t love it, at least I know I will enjoy and appreciate it.&lt;/p&gt;
&lt;p&gt;Mike might not be one of those artists who get a vast amount of attention. However, his work on the whole has really resonated with me. I hope this short tour gives you some background and insight into an artist who is multi-layered, complex and deeply moving.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The Music of Mike Oldfield&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>My Workflow Today</title><link>https://deliciousreverie.co.uk/posts/my-workflow-today/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/my-workflow-today/</guid><description>Workflows and tools are constantly changing. The tools we use and the workflow we have means we&apos;ve simplified our build process and cut out some of the repetitive tasks, so that we can concentrate on simply doing the stuff we love as well as we can. This is my current set up: </description><pubDate>Tue, 17 Nov 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Workflows and tools are constantly changing. The tools we use and the workflow we have means we&apos;ve simplified our build process and cut out some of the repetitive tasks, so that we can concentrate on simply doing the stuff we love as well as we can. This is my current set up:&lt;/p&gt;
&lt;h3&gt;MAMP — for developing locally &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#mamp-for-developing-locally&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Despite the learning curve, developing locally before you push your changes live avoids so many potential problems, and allows me to use some great pre-build tools that just don&apos;t work on servers that I have available.&lt;/p&gt;
&lt;p&gt;I use MAMP because it was orginally built for the Mac and was the simplest one to use — and I tried a few. I recently bought the Pro version because it allows you to create virtual hosts. Virtual hosts make it much easier to go live (a lot less absolute URLs to change).&lt;/p&gt;
&lt;h3&gt;Sublime Text — for coding &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#sublime-text-for-coding&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve tried various tools for witing code, but keep coming back to Sublime Text. I really wanted to like Atom, but it was just too slow and buggy at the time. Sublime Text 3&apos;s extensions make a really great workflow that I&apos;ve got really comfortable with (for the moment...)&lt;/p&gt;
&lt;h3&gt;Sublime Text Extensions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#sublime-text-extensions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4&gt;Material theme &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#material-theme&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Material Theme — because I love dark backgrounds, I find it helps me to concentrate. Plus it has folder icons that help me to see what kind of file I&apos;m looking at at a glance. I find it&apos;s quite common for a theme to have great code hinting in either JS or SASS, but rarely both. Material seems to get a good balance between the two.&lt;/p&gt;
&lt;h4&gt;GitGutter &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#gitgutter&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This plugin allows you to see what lines have been changed since your last git commit. Really handy if you can&apos;t remember what you&apos;ve done, or your working with a colleague who doesn&apos;t use git (it happens!).&lt;/p&gt;
&lt;h4&gt;Grunt — for repetitive task running &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#grunt-for-repetitive-task-running&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I&apos;m feeling a bit of pressure to move to Gulp for task running, but I have a well established workflow for Grunt which I don&apos;t want to rebuild at the moment ... so I&apos;m going to stick with what I have!&lt;/p&gt;
&lt;h4&gt;Grunt-sass &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#grunt-sass&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Grunt-sass uses the libSass version of SASS compiler, which is now at feature parity with Ruby Sass. Although it does mean that compiling is much faster, it&apos;s not the reason I went with Grunt Sass. Not having Ruby as a dependency for my project is cleaner and less to maintain and could mean less potential conflicts in the future.&lt;/p&gt;
&lt;h4&gt;PostCSS / Autoprefixer &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#postcss-autoprefixer&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I moved to PostCSS for the use of Autoprefixer, which, as the name suggests, allows you to add browser-specific prefixes to your CSS. You can even specify how many previous browser versions you want to support. No more trawling through &lt;a href=&quot;http://caniuse.com/&quot;&gt;caniuse.com&lt;/a&gt; to figure out what prefixes you need. Want to clean up your code and remove old prefixes? You can do that really quickly with this tool.&lt;/p&gt;
&lt;p&gt;PostCSS&apos;s creator seems to want PostCSS to do much more, but I find the documentation scant and folder structure (multiple nested &apos;node_modules&apos; folders) a little jarring, so I haven&apos;t used it for anything else, although it shows great potential.&lt;/p&gt;
&lt;h3&gt;GitHub Desktop — for version control &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#github-desktop-for-version-control&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GitHub Desktop simplifies Git for me, something which I&apos;m still not comfortable to use solely in the command line (although I do occaionally).&lt;/p&gt;
&lt;p&gt;It does bother me that I&apos;m only able to use GitHub accounts with GithubDesktop, so I&apos;ve been investigating using something like Tower. I don&apos;t think I&apos;ve got a good enough use case for this purchase at the moment really, and I do tend to think I should get used to the command line interface when it comes to GIT.&lt;/p&gt;
&lt;h3&gt;Cyberduck — for file upload / download &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#cyberduck-for-file-upload-download&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I love the simplicity of Cyberduck, plus the whimsical yellow rubber duck sitting in your task bar raises a few eyebrows occasionally...&lt;/p&gt;
&lt;h3&gt;Trello — for project management &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#trello-for-project-management&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Trello is a great project management tool. It&apos;s so flexible; for example, I&apos;ve set it up as a Kanban board but if you prefer you can set up lists for today, tomorrow future etc or any way you like.&lt;/p&gt;
&lt;p&gt;I will usually have 2 main boards: my current project board, and Project Overview board so I can track different projects and easily get a birds-eye view of where I am with each of my projects.&lt;/p&gt;
&lt;h3&gt;BugHerd — for feedback and bug tracking &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#bugherd-for-feedback-and-bug-tracking&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I love BugHerd. With this tool, you can include a script in your dev site and get users / clients / colleagues to click on the site, make annotations and collate these onto a project board. Then you can assign tasks out to different people or open a discussion about why you&apos;ve built it this way...&lt;/p&gt;
&lt;h3&gt;Affinity Photo &amp;amp; Designer — for wire framing &amp;amp; designing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#affinity-photo-and-designer-for-wire-framing-and-designing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Affinity is not as easy or as professional as using Photoshop and Illustrator — yet. I have become disillusioned with Adobe lately.&lt;/p&gt;
&lt;p&gt;Affinity are providing an attractive solution which does work well despite the learning curve, and I&apos;m really looking forward to their direct competitor to InDesign which should be in public beta soon.&lt;/p&gt;
&lt;h2&gt;What about you? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/my-workflow-today/#what-about-you&quot;&gt;#&lt;/a&gt;&quot;&lt;/h2&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:My Workflow Today&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Working with styled components: multiple nested properties</title><link>https://deliciousreverie.co.uk/posts/nesting-styled-components-properties/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/nesting-styled-components-properties/</guid><description>Destructuring is a common pattern when it comes to using styled components in complex situations. But it can be hazardous to clearly identify nesting. Here are a few suggestions. </description><pubDate>Sat, 01 May 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve seen this fairly frequently when it comes to using shared media queries. sometimes you want to set the width of an element based on one property, but you need to access the theme too.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const StyledContainer = styled.div`
  ${(props) =&amp;gt;
    props.narrow
      ? css`
          @media (min-width: ${props.theme.desktopMin}) {
            max-width: 48rem;
          }
        `
      : css`
          @media (min-width: ${props.theme.desktopMin}) {
            max-width: 32rem;
          }
        `}
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could destructure them like this however it can become difficult to read, particularly the position of the closing bracket, which becomes difficult to see with the template literal syntax:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const StyledContainer = styled.div`
    ${({ narrow, theme })) =&amp;gt; narrow ? css `
        @media (min-width: ${theme.desktopMin}) {
            max-width: 48rem;
        }
      ` : css `
          @media (min-width: ${theme.desktopMin}) {
              max-width: 32rem;
          }
      `
    }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I think it could be better not to destructure. You could easily start to think about what would happen when you have nested ternaries, which in my time I have spent too long trying to unpick.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Working with styled components: multiple nested properties&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Unit Test your React apps</title><link>https://deliciousreverie.co.uk/posts/netmag-unit-test/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/netmag-unit-test/</guid><description>Originally published in print for Net Magazine issue 320. Do you want production-ready code without going to production first? This article shows how it&apos;s possible with React Testing Library. </description><pubDate>Fri, 28 Feb 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Originally published in print for &lt;a href=&quot;https://www.creativebloq.com&quot;&gt;Net Magazine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Do you really know what your code does what it is supposed to do? Have you tested it in the browser? What if you haven&apos;t, or you can&apos;t test everything, and it breaks in production?&lt;/p&gt;
&lt;p&gt;A testing library is a group of utilties that developers use to write individual tests on our application components. Some of the principle parts of a test are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Description: describe what the test is about&lt;/li&gt;
&lt;li&gt;Use / Render: uses the component in an environment where it can be tested&lt;/li&gt;
&lt;li&gt;Mocking: create pretend functions, so that you can check your assumptions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this article I&apos;m going to show some examples from React Testing Library to help you get started with this valuable way of improving the robustness of your code output.&lt;/p&gt;
&lt;h2&gt;Getting Started with React Testing Library &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#getting-started-with-react-testing-library&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;m going to use create-react-app for this demo, because it already comes preconfigured with the testing library. If you&apos;re using Gatsby or a custom setup, there might be some configuration you need to run through before you start using the testing library.&lt;/p&gt;
&lt;p&gt;To start let&apos;s create a new app. If you have a recent version of Node.js already, you can run the following command without installing anything else globally:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx create-react-app netmag-javascript-testing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now open the folder in your code editor.&lt;/p&gt;
&lt;p&gt;Create-react-app is great because you can already run yarn test and see that one test is already passing. This command will also keep watching for file changes, so you can see straight away whether or not the tests you write are passing.&lt;/p&gt;
&lt;p&gt;Let&apos;s start writing some tests!&lt;/p&gt;
&lt;h2&gt;What to Test &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#what-to-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine we have a simple component, say a button with some state. What are some of the things that need testing in a component like this?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The appearance of the component&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We don&apos;t want anything to change unexpectedly after we&apos;ve written our component. So we&apos;re going to write a snapshot test to capture how it renders. Then, if anything changes we will see it quickly, without a manual or visual test. This is great for components that consist of many smaller components: you can see quickly when (and where) it&apos;s appearance has been affected.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The different branches that render&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because we could have 2 or more different outputs, we need to test that it&apos;s rendering all of them correctly, not just one. So we need to simulate a click event and have another snapshot test for the way it renders after this branch of code has been run.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;That functions get called as expected&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We want to ensure that the code we wrote to call another function works as we assume it will. But since that function is an external dependency, we don&apos;t want to test that here. Our tests should encapsulate only the functionality we want them to.&lt;/p&gt;
&lt;h2&gt;Writing our First Test &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#writing-our-first-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&apos;s write our first test. Create a new file called MyComponent.unit.test.js in the same folder as the component. By adding test.js at the end, it&apos;ll be automatically picked by the testing library. The contents of that file are below:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &apos;react&apos;

import { render } from &apos;@testing-library/react&apos;

import MyComponent from &apos;./MyComponent&apos;

describe(&apos;the &amp;lt;MyComponent /&amp;gt;&apos;, () =&amp;gt; {

    // tests go here

})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing I want to draw your attention to is the &lt;code&gt;describe()&lt;/code&gt; function, which takes 2 arguments: the first is a string that you can use to better describe as a string of text what your test is going to be doing. In our case we&apos;ve said simply that it should render. This is very useful when someone else looks at your code, or you have to remember what you did at a later stage. Writing good &quot;describe&quot; statements is a form of code documentation, and another good reason for writing tests!&lt;/p&gt;
&lt;p&gt;The second argument are your tests. The &lt;code&gt;describe()&lt;/code&gt; function will run all of these tests one after the other.&lt;/p&gt;
&lt;h2&gt;Cleanups &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#cleanups&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&apos;s introduce helper function called &lt;code&gt;beforeEach()&lt;/code&gt;. We need to use this because each time we do something with the component, we want a fresh copy without the props we previously had passed to it still existing in the component. Or we might need to re-render the component. beforeEach() does that for us, and we can pass it the cleanup function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { render, cleanup } from &apos;@testing-library/react&apos;
...
describe(&apos;the component should render&apos;, () =&amp;gt; {
  beforeEach(cleanup)
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Writing a Snapshot Test &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#writing-a-snapshot-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this step, we&apos;re going to &quot;mount&quot; our component (or render it).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;describe(&apos;the component should render&apos;, () =&amp;gt; {
  beforeEach(cleanup)
  it(&apos;renders with basic props&apos;, () =&amp;gt; {
    render(&amp;lt;MyComponent /&amp;gt;)
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This render gives us access to all of the rendered properties of the compiled component. It might be good to drop this into a console.log()so you can see more clearly what it does.&lt;/p&gt;
&lt;p&gt;If you do, you&apos;ll see that there&apos;s a few useful properties we can take advantage of here. I&apos;m going to make an assertion (make a testable declaration) and test it by extracting the container. The container &quot;contains&quot; the DOM nodes (all of the HTML) associated with the component.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;renders with basic props&apos;, () =&amp;gt; {
    const { container } = render(&amp;lt;MyComponent /&amp;gt;)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have access to the container, how do I tell that it&apos;s rendered according to my assertion? By adding a snapshot test.&lt;/p&gt;
&lt;p&gt;A snapshot is like a photograph. It takes a snapshot of our component at a specific point in time. Then, whenever we make alterations to the code, we can see if it still matches the original snapshot. If it does, we can be confident that nothing has changed in the component.&lt;/p&gt;
&lt;p&gt;However, if it doesn&apos;t we might have uncovered an issue that originated in another component, one that we might not have spotted previously:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  it(&apos;renders with basic props&apos;, () =&amp;gt; {
    const { container } = render(&amp;lt;MyComponent /&amp;gt;
    expect(container).toMatchSnapshot()
  )
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing Properties &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#testing-properties&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Props, or properties, of a component can be tested with snapshots too. Testing the different props you provide to your component will give you greater coverage and confidence ... you never know when a requirement is going to mean your component&apos;s props are refactored and the final output will change:&lt;/p&gt;
&lt;p&gt;Add this object to the top of your file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const lightProperties = {
    backgroundColour: &apos;white&apos;,
    textColour: &apos;darkblue&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We define the properties in an object and then use the spread operator (three dots followed by the object name, ...lightproperties) in this way because we can only pass one argument in when we render in this way. It&apos;s also useful to see what properties you&apos;re passing in isolation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    it(&apos;renders with basic props&apos;, () =&amp;gt; {
        const { container } = render(&amp;lt;MyComponent /&amp;gt;
      )
     expect(container).toMatchSnapshot()
    })
    it(&apos;renders with the light version props&apos;, () =&amp;gt; {
        const { container } = render(
            &amp;lt;MyComponent { ...lightProperties } /&amp;gt;
        )
        expect(container).toMatchSnapshot()
    })
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Testing Changes in the UI &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#testing-changes-in-the-ui&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine that our component has a button, and you want to make sure that something happens when the button is clicked.&lt;/p&gt;
&lt;p&gt;You might think that you want to test the state of the application, for example, you might be tempted to test that the state has updated. However, that&apos;s not the object of these tests.&lt;/p&gt;
&lt;p&gt;This introduces us to an important concept in using a testing library: we&apos;re not here to test the state, or the way our component works. We&apos;re here to test how people are going to use the component, and that it meets their expectations.&lt;/p&gt;
&lt;p&gt;So whether or not the state has updated is immaterial; what we want to test is what the outcome of that button press is.&lt;/p&gt;
&lt;p&gt;Let&apos;s imagine we&apos;re testing the outcome of a function that changes the UI from dark mode to light mode. Here&apos;s the component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const modeToggle = () =&amp;gt; {
    const [mode, setMode] = useState[&apos;light&apos;]
   const toggleTheme = () =&amp;gt; {
     if (theme === &apos;light&apos;) {
       setTheme(&apos;dark&apos;)
     } else {
       setTheme(&apos;light&apos;)
     }
   }
    return (
        &amp;lt;ToggleButton data-testid=&quot;mode-toggle&quot; lightMode={mode} onClick={toggleMode}&amp;gt;
          Toggle mode
        &amp;lt;/ToggleButton&amp;gt;
    )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, we should add a test id onto the button so that we can find it in the render phase:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return (
    &amp;lt;ToggleButton
      data-testid=&quot;mode-toggle&quot;
      lightMode={mode}
      onClick={toggleMode}
     &amp;gt;
      Toggle mode
    &amp;lt;/ToggleButton&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Did you notice we added the new attribute data-testid to the button? Here&apos;s how you might test that.&lt;/p&gt;
&lt;p&gt;First, import a new function, fireEvent from the testing library:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { cleanup,
          fireEvent,
          render
} from &apos;@testing-library/react&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can use that function to test that there are changes in the UI, and that those changes are consistent:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;renders with basic props&apos;, () =&amp;gt; {
    const { container } = render(&amp;lt;ToggleButton /&amp;gt;
  )
 expect(container).toMatchSnapshot()
})
it(&apos;renders the light UI on click&apos;, () =&amp;gt; {
    const { container, getByTestId } = render(&amp;lt;ToggleButton /&amp;gt;)
    fireEvent.click(getByTestId(&apos;mode-toggle&apos;))
    expect(container).toMatchSnapshot()
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is great, we don&apos;t have to manually go to the site and look around, then click the button and look around a second time, which you might admit, you&apos;ll likely forget or miss something! Now we can have confidence that, given the same input, we can expect the same output in our component.&lt;/p&gt;
&lt;h3&gt;A Note about Test IDs &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#a-note-about-test-ids&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Personally, I dislike using &lt;code&gt;data-testid&lt;/code&gt; to find something in the DOM. After all, the object of tests is to mimic what the user is doing, and to test what happens when they do. &lt;code&gt;data-testid&lt;/code&gt; feels like a bit of a cheat -- although &lt;code&gt;data-testid&lt;/code&gt;s will likely come in handy for your QA Engineer (see the box &quot;The Role of Quality Assurance Engineers&quot;).&lt;/p&gt;
&lt;p&gt;Instead we could use &lt;code&gt;getByText()&lt;/code&gt; and pass in the text of our button. That would be a lot more behaviour specific.&lt;/p&gt;
&lt;h2&gt;Mocking and Spying &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#mocking-and-spying&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes we need to test a call to a function, but that function is outside the scope of the test. For example, I have a separate module that contains a function that calculates the value of pi to a certain number of decimals.&lt;/p&gt;
&lt;p&gt;I don&apos;t need to test what the result of that module is. I need to test that my function does as expected. For more information about why this is, please see the box &quot;unit and integration tests&quot;. In this case, we could &quot;mock&quot; that function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const getPiValue = jest.fn()
it(&apos;calls the function on click&apos;, () =&amp;gt; {
    const { container, getByTestId } = render(&amp;lt;ToggleButton /&amp;gt;)
    fireEvent.click(getByTestId(&apos;mode-toggle&apos;))
    expect(getPiValue).toHaveBeenCalledTimes(1)
  )
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function&lt;code&gt;toHaveBeenCalledTimes()&lt;/code&gt; is one of the many helper functions in the testing library that allow us to test the output of functions. This allows us not only to scope our tests only to the module we want to test, but also allow us to &quot;spy&quot; on, or see what our function does when it calls that function.&lt;/p&gt;
&lt;h2&gt;Start Testing your React Applications Today &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#start-testing-your-react-applications-today&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Writing tests can seem a little daunting to start with. I hope this tutorial has given you a little more confidence to try it. Since I started writing tests for my applications, I really can&apos;t go back: I can rest easier, knowing I&apos;m leaving behind a much better legacy for those who will use my work in the future.&lt;/p&gt;
&lt;p&gt;For more ideas about how to test your components take a look at one of the links below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://react-testing-examples.com&lt;/li&gt;
&lt;li&gt;https://testing-library.com/docs/react-testing-library/intro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&apos;re looking for some courses to help you get started, the one by Kent C Dodds (who wrote and maintains React Testing Library) is popular:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://testingjavascript.com/&quot;&gt;https://testingjavascript.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I also enjoyed this one on Level Up Tutorials, it&apos;s the one that got me started writing tests for my code:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.leveluptutorials.com/tutorials/react-testing-for-beginners&quot;&gt;https://www.leveluptutorials.com/tutorials/react-testing-for-beginners&lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Unit and Integration Tests &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#boxout:-unit-and-integration-tests&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are generally two types of tests we talk about in the context of code tests:&lt;/p&gt;
&lt;h3&gt;Unit Testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#unit-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is to test the units of code, or each module in isolation. They shouldn&apos;t call other modules in your project, instead you should mock these modules so that the unit test is able to run.&lt;/p&gt;
&lt;h3&gt;Integration Testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#integration-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When two or more modules are used in conjunction, then it&apos;s a good practice to write an integration test. These test how modules work together. Well written integration tests can identify issues when you&apos;re using modules as part of a larger piece of work.&lt;/p&gt;
&lt;p&gt;These are the two kinds of tests that developers are usually responsible for writing. There are other kinds of tests that QA Engineers write (see the box &quot;The Role of Quality Assurance Engineers&quot;).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: What is Test Driven Development? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#boxout:-what-is-test-driven-development&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TDD, or test-driven development, is a testing discipline that forces you to think about edge cases when you&apos;re in the process of development.&lt;/p&gt;
&lt;p&gt;You start out with a hypothesis that forms the basis for your tests. This could be something like &quot;it renders a button&quot;, &quot;it renders a button using the dark mode ui on click&quot;, then you go ahead and write your tests.&lt;/p&gt;
&lt;p&gt;Only after you have at least 3 robust tests that you can use to verify what your component&apos;s behaviour should be, then you start coding the actual component.&lt;/p&gt;
&lt;p&gt;I like this concept because it forces you to think about the structure of your component from completely different angles. You&apos;re not trying to preserve the code you already wrote, so you can find different ideas more easily.&lt;/p&gt;
&lt;p&gt;Be careful though: you can write tests that will pass under any circumstances! For this reason, three or more tests are often required that allow you to &quot;triangulate&quot;, or effectively define the functionality of your component, before you can start building it.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: The Role of Quality Assurance Engineers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-unit-test/#boxout:-the-role-of-quality-assurance-engineers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Quality Assurance engineers are the secret superpower of web development. A good QA Engineer and a strong QA process can save development time, reduce issues in production, and have been known to save the reputation of entire development teams ... and the companies they work for!&lt;/p&gt;
&lt;p&gt;QA Engineers have totally different mindset to developers: Developers naturally build against assumptions about how something should work, and can only test these assumptions. QAs on the other hand, challenge these assumptions, using every trick they can think of to try to break the code in some way.&lt;/p&gt;
&lt;p&gt;This ensures that your customers aren&apos;t acting as your QAs, causing you to lose money and good will in a very public manner.&lt;/p&gt;
&lt;p&gt;A dedicated Quality Assurance engineer will write an automated tests suite that check the expected functionality of the built application, predominantly on a staging domain, and end-to-end tests that check users can carry out goals that you expect them to achieve.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Unit Test your React apps&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Osip Mandelshtam / Philip Nikolayev</title><link>https://deliciousreverie.co.uk/posts/nikolayev-mandelshtam/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/nikolayev-mandelshtam/</guid><description>Since reading Boris Pasternak&apos;s Doctor Zhivago, I&apos;ve been deeply moved by his poetry. I&apos;ve just discovered the wonderful contemporary Russian poet Osip Mandelshtam, who, like Pasternak, evokes some wonderful imagery and delightful reveries.</description><pubDate>Thu, 02 Apr 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Since reading Boris Pasternak&apos;s Doctor Zhivago, I&apos;ve been deeply moved by his poetry. I&apos;ve just discovered the wonderful contemporary Russian poet Osip Mandelshtam, who, like Pasternak, evokes some wonderful imagery and delightful reveries.&lt;/p&gt;
&lt;p&gt;Philip Nikolayev has a wonderful command of the English language, and his translations of Mandelshtam&apos;s poems is really delightful, particularly in his use of alliteration. Here&apos;s my favourite one:&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Insomnia, Homer, taut sails: my lips have lisped
Down to the middle the detailed list of ships,
That long brood and angular train of cranes
That rose above Hellas once on wings of waves.
A wedge of cranes into far foreign lands -
Divine white froth forming upon kings&apos; heads -
Where have you hoist sail to/ If it were not for Helen
What would Troy alone be to you, Achaean men?
The sea and Homer - as all - are moved by love;
Which should I hark? Homer the first to fall
Silent, the black sea&apos;s cries still rhapsodize
Reaching to where I lie with sleep-filled eyes.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;If you want to find out more about Osip Mandelshtam, there&apos;s a dedicated facebook page. The translator, Philip Nicolayev, is curator of Fulcrum: an annual of poetry and aesthetics.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Osip Mandelshtam / Philip Nikolayev&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Normalizing data using array.prototype.reduce</title><link>https://deliciousreverie.co.uk/posts/normalizing-data-using-reduce/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/normalizing-data-using-reduce/</guid><description>Normalizing data using array.prototype.reduce</description><pubDate>Tue, 18 Jun 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;reduce() has terrified me for some time! I hadn&apos;t wanted to touch this new array method because I couldn&apos;t quite follow what each of it&apos;s properties meant. But recently I had an opportunity to deep dive into this function. Here&apos;s how I refactored an object to get the results I needed:&lt;/p&gt;
&lt;p&gt;Edit: I realize this article doesn&apos;t really show you enough about how to use reduce, but it&apos;s still coming up in my SEO keywords quite frequently. So here&apos;s a more thorough article on the topic: &lt;a href=&quot;https://dev.to/ra1nbow1/5-ways-to-use-the-reduce-method-in-js-1k8h&quot;&gt;https://dev.to/ra1nbow1/5-ways-to-use-the-reduce-method-in-js-1k8h&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m beginning to realise how important good API design is. One API I call for a project has only one endpoint: a list of items. Each item is quite large, with loads of tags and categories. Yet I can&apos;t query the API by any of those.&lt;/p&gt;
&lt;p&gt;This became an issue when I had a requirement to display them by categories. The real trick was that the category was inside a nested object. AND it had to be matched and replaced by a different value before I displayed it.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of the object once I&apos;d parsed the JSON:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    ---
title: &quot;some title&quot;,
    categories: {
        team: &quot;teamone&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I needed for it to be displayed was this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    teamone: {
        [{
            ---
title: &quot;some title&quot;,
            categories: {
                team: &quot;teamone&quot;
            }
        }, {
            ...
        }, ]
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 1: Map &amp;amp; Replace values &lt;a href=&quot;https://deliciousreverie.co.uk/posts/normalizing-data-using-reduce/#step-1:-map-and-replace-values&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first thing I did was to map the new values of the team using the following method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;itemsTeamsMap {
                &quot;teamone&quot; : &quot;team1&quot;,
                &quot;teamtwo&quot; : &quot;team2&quot;,
}

const itemsMapped = []

  const filteredItems = edges.map(item =&amp;gt; {
    item.node.categories.team = itemsTeamsMap[item.node.categories.team]
    itemsMapped.push(item)
  })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was especially important because it wasn&apos;t a 1:1 match, some teams would be reassigned to other new values.&lt;/p&gt;
&lt;h2&gt;Step 2: Sort items using Reduce() &lt;a href=&quot;https://deliciousreverie.co.uk/posts/normalizing-data-using-reduce/#step-2:-sort-items-using-reduce()&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once I had that, I needed to sort my items by the new team values I&apos;d given them so that grouping would be easier using a bubble sort:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const sortedItems = ItemsMapped.reduce((acc, current) =&amp;gt; {
    acc[current.node.categories.team] = acc[current.node.categories.team] || []
    acc[current.node.categories.team].push(current)
    return acc
  }, Object.create(null))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It took me a good while to get my head around the reduce function, something that&apos;s been called the most powerful function in JavaScript. The first argument is the accumulator value, or something which accumulates each iteration of the function.&lt;/p&gt;
&lt;p&gt;The second value is the current item you&apos;re iterating over, which you can modify with any function you put into the body.&lt;/p&gt;
&lt;p&gt;Here&apos; I&apos;m pushing the current value into the team item of each item in the accumulator and then creating an object from that value.&lt;/p&gt;
&lt;p&gt;The result of this function on my original data is this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    team1: [{
            title: &apos;some title&apos;,
            categories: {
                team: &apos;team1&apos;
                    ...
            }
            ...
        }, {
            ...
        },
        ...
    ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a good deal closer to the final object that I needed. Of course though, I needed an array of objects that I could iterate over in my JSX template...&lt;/p&gt;
&lt;h2&gt;Step 3: Array of Objects &lt;a href=&quot;https://deliciousreverie.co.uk/posts/normalizing-data-using-reduce/#step-3:-array-of-objects&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;  const finalisedItems = Object.entries(sortedItems).reduce(
    (acc, [team, items]) =&amp;gt;
      acc.concat({
        team,
        items,
      }),
    []
  )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I love the new object methods introduced recently to JavaScript. They allow you to do so much more with objects natively ... in this function, for each entry of the object I&apos;m passing in, it will concat the team into an array of objects, where team is the title, and each group of items is an array of objects inside of that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[{
        team: &apos;team1&apos;,
        items: [{
                title: &apos;some title&apos;,
                categories: {
                    team: &apos;team1&apos;
                        ...
                }
                ...
            },
            ...
        ]
    }, {
        team: &apos;team2&apos;,
        items: [{
                title: &apos;some other title&apos;,
                categories: {
                    team: &apos;team2&apos;
                        ...
                }
                ...
            },
            ...
        ]
    },
    ...
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Step 4: templatingNow I can iterate over each team, then each item in that team, outputting semantic elements (&lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;s) for each item:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {finalisedItems.map(itemsTeam =&amp;gt; (
        &amp;lt;div key={itemsTeam.team}&amp;gt;
          &amp;lt;h3&amp;gt;{itemsTeam.team}&amp;lt;/h3&amp;gt;
          &amp;lt;ul&amp;gt;
            {itemsTeam.items.map(item =&amp;gt; (
              &amp;lt;li key={item.node.id}&amp;gt;
                &amp;lt;a to={item.node.url}&amp;gt;
                  {item.node.text}
                &amp;lt;/a&amp;gt;
              &amp;lt;/li&amp;gt;
            ))}
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Going the extra mile in normalising this object isn&apos;t a challenge I relished, however I have improved a lot as a developer by working through and providing a solution.&lt;/p&gt;
&lt;p&gt;I learned that trying to do a faster job didn&apos;t yield the correct results, so my fastest solution was by far the best.&lt;/p&gt;
&lt;p&gt;Sometimes you&apos;ve just got to knuckle down, go in for the long haul, and you&apos;ll find you&apos;ve come out the other end similarly restructured (in a good way!)&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Normalizing data using array.prototype.reduce&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>I&apos;m Not Proud</title><link>https://deliciousreverie.co.uk/posts/not-proud/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/not-proud/</guid><description>I&apos;m not proud of the code I wrote today. In fact, I hated it. But that&apos;s okay, it did what it needed to. And I&apos;m going to redo it tomorrow. </description><pubDate>Wed, 30 Jan 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Today was a stressful day. I was working to a set deadline on which a lot of other tasks depended. I felt that if I failed to achieve my work, there would be a cascade effect that could lead to delaying the release of a product I have been working on.&lt;/p&gt;
&lt;p&gt;I didn&apos;t want that to happen. But I&apos;m not proud of what I did to avoid it.&lt;/p&gt;
&lt;p&gt;I was working with our best designer, who had begun to refactor a module but needed some help with some finer structure and more complicated CSS that would display the images in the way that the Creative Director had approved.&lt;/p&gt;
&lt;p&gt;In order to hit my deadline I hacked the module I was working on. I added props that were not semantically correct. I added images directly into the module itself instead of passing them down from the page. I wrote no tests and defined no types.&lt;/p&gt;
&lt;p&gt;After another round of tweaks and some refinements, it was ready for code review. I rushed it past, asking someone I knew would be too busy to tell me what I should&apos;ve done instead. I&apos;m sure he raised an eyebrow or two when he approved it.&lt;/p&gt;
&lt;p&gt;Miraculously, it passed QA and was released that same day.&lt;/p&gt;
&lt;p&gt;I still hate myself whenever I think of that code.&lt;/p&gt;
&lt;h2&gt;Unexpected Consequences &lt;a href=&quot;https://deliciousreverie.co.uk/posts/not-proud/#unexpected-consequences&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We started a new sprint today, and one of the backlog items is a refactor of that module. This time I&apos;m going to do it correctly, passing down the image from the page, removing the duplicate CSS and finding a way for our designer to pass specific CSS so they can position the image as they want.&lt;/p&gt;
&lt;p&gt;What I didn&apos;t expect was to hear that our user engagement has increased significantly. Conversions are up by about 11%, and the project is much closer to launch than it would have been without those hacky changes I made.&lt;/p&gt;
&lt;p&gt;So no, I&apos;m not at all proud of the code I wrote today.&lt;/p&gt;
&lt;p&gt;But it helped the whole project move forward. It might even release very soon. And now I&apos;m going to re-do it properly.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:I&apos;m Not Proud&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Observing Element Changes in the Shadow DOM</title><link>https://deliciousreverie.co.uk/posts/observing-element-changes-in-the-shadow-dom/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/observing-element-changes-in-the-shadow-dom/</guid><description>DOM content isn&apos;t always loaded in top-to-bottom. Custom elements sometimes need to wait for API calls, and sometimes you might instantiate some of the DOM elements via JavaScript. Here&apos;s a handy function to observe those changes</description><pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;DOM content isn&apos;t always loaded in top-to-bottom. Custom elements sometimes need to wait for API calls, and sometimes you might instantiate some of the DOM elements via JavaScript. Here&apos;s a handy function to observe those changes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Working with large Drupal sites is interesting from a management perspective. It does do a lot of AJAX calls to the backend, and sometimes that might result in the DOM structure changing because of that call.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; I&apos;ve got an &lt;a href=&quot;/posts/waiting-for-an-element-with-intersection-observer&quot;&gt;updated article&lt;/a&gt; which simplifies this example, and talks about the implementation in detail.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;But this happens a lot in complex applications, and isn&apos;t limited to apps managed by PHP. For example, sometimes we need to, or want to do something similar to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const container = document.querySelector(&apos;put-stuff-here&apos;);

container.appendChild(`&amp;lt;div&amp;gt;${someVarible}&amp;lt;/div&amp;gt;`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or sometimes it&apos;s more complex, where you have a custom element that has to be registered, and perhaps has to fetch some data before it renders.&lt;/p&gt;
&lt;p&gt;I found myself needing to hook into these state changes a few times recently. Quite often, a &lt;code&gt;mutationObserver&lt;/code&gt; was enough to do the trick. However it does contain quite a bit of boilerplate:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Wait for an element to be added to the DOM.
 * @param {ShadowRoot | Document} root - The root to observe.
 * @param {string} selector - The selector of the element to wait for.
 * @returns {Promise&amp;lt;HTMLElement | any&amp;gt;} - A promise that resolves with the element when it is added to the DOM.
 */
function waitForElement(root, selector) {
  return new Promise((resolve) =&amp;gt; {
    let element = root.querySelector(selector);
    if (element) {
      resolve(element);
    } else {
      const observer = new MutationObserver((mutations) =&amp;gt; {
        mutations.forEach((mutation) =&amp;gt; {
          if (mutation.type === &apos;childList&apos;) {
            element = root.querySelector(selector);
            if (element) {
              observer.disconnect();
              resolve(element);
            }
          }
        });
      });

      observer.observe(root, { childList: true, subtree: true });
    }
  });
}
export default waitForElement;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is more or less &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver&quot;&gt;straight from the docs&lt;/a&gt;, but 2 things are missing: shadowDOM support (something I use a lot) and some way of resolving the promise to stop it from running indefinitely.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const observer = new MutationObserver((mutations) =&amp;gt; {
  mutations.forEach((mutation) =&amp;gt; {
    if (mutation.type === &quot;childList&quot;) {
      mutation.addedNodes.forEach((node) =&amp;gt; {
        if (node.nodeType === Node.ELEMENT_NODE) {
          if (node.matches(selector)) {
            observer.disconnect();
            clearTimeout(timeoutId); // Clear the timeout
            resolve(node);
          } else if (node.shadowRoot) {
            const shadowElement = node.shadowRoot.querySelector(selector);
            if (shadowElement) {
              observer.disconnect();
              clearTimeout(timeoutId); // Clear the timeout
              resolve(shadowElement);
            } else {
              observer.observe(node.shadowRoot, { childList: true, subtree: true });
            }
          }
          element = root.querySelector(selector);
          if (element) {
            observer.disconnect();
            clearTimeout(timeoutId); // Clear the timeout
            resolve(element);
          }
        }
      });
    }
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My issue with this is there are lots of nested &lt;code&gt;if&lt;/code&gt; statements going on here, I might try to tidy that up soon.&lt;/p&gt;
&lt;p&gt;But essentially it works the same by checking for the existence of a &lt;code&gt;shadowRoot&lt;/code&gt; and resolving the element if it&apos;s found there.&lt;/p&gt;
&lt;p&gt;There&apos;s only one other thing we need to fix: to ensure it doesn&apos;t keep running indefinitely if the element doesn&apos;t show up in a reasonable amount of time.&lt;/p&gt;
&lt;h2&gt;Timeout&lt;/h2&gt;
&lt;p&gt;Ahh good old timeouts. I&apos;m trying to think if there&apos;s a better way to do this.&lt;/p&gt;
&lt;p&gt;Let&apos;s wait for a timeout of 1 second to expire and then &lt;code&gt;reject&lt;/code&gt; and disconnect the observer:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;observer.observe(root, { childList: true, subtree: true });

// Set a timeout to reject the promise if the element is not found within 1 second
const timeoutId = setTimeout(() =&amp;gt; {
  observer.disconnect(); // Stop observing when the timeout expires
  reject(new Error(&quot;Element not found&quot;));
}, 1000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Putting this together we can observe elements across the normal DOM and the shadow DOM, and provide a slim implementation on top of &lt;code&gt;mutationObserver&lt;/code&gt; so we can be certain the element exists before we try to do something like attach a listener or modify it in some other way.&lt;/p&gt;
&lt;p&gt;Here&apos;s how to implement it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const myElement = waitForElement(parent, &apos;my-element&apos;);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s the full code, with an additional parameter to change the timeout if you like. Happy copypasta!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Wait for an element to be added to the DOM or shadow DOM.
 * @param {ShadowRoot | Document} root - The root to observe.
 * @param {string} selector - The selector of the element to wait for.
 * @param {number} [timeout] - The time in milliseconds to wait before rejecting the promise.
 * @returns {Promise&amp;lt;HTMLElement | Node&amp;gt;} - A promise that resolves with the element when it is added to the DOM or shadow DOM.
 */
function waitForElement(root, selector, timeout = 1000) {
  return new Promise((resolve, reject) =&amp;gt; {
    let element = root.querySelector(selector);
    if (element) {
      resolve(element);
    } else {
      const observer = new MutationObserver((mutations) =&amp;gt; {
        mutations.forEach((mutation) =&amp;gt; {
          if (mutation.type === &quot;childList&quot;) {
            mutation.addedNodes.forEach((node) =&amp;gt; {
              if (node.nodeType === Node.ELEMENT_NODE) {
                if (node.matches(selector)) {
                  observer.disconnect();
                  clearTimeout(timeoutId); // Clear the timeout
                  resolve(node);
                } else if (node.shadowRoot) {
                  const shadowElement = node.shadowRoot.querySelector(selector);
                  if (shadowElement) {
                    observer.disconnect();
                    clearTimeout(timeoutId); // Clear the timeout
                    resolve(shadowElement);
                  } else {
                    observer.observe(node.shadowRoot, { childList: true, subtree: true });
                  }
                }
                element = root.querySelector(selector);
                if (element) {
                  observer.disconnect();
                  clearTimeout(timeoutId); // Clear the timeout
                  resolve(element);
                }
              }
            });
          }
        });
      });

      observer.observe(root, { childList: true, subtree: true });

      // Set a timeout to reject the promise if the element is not found within 1 second
      const timeoutId = setTimeout(() =&amp;gt; {
        observer.disconnect(); // Stop observing when the timeout expires
        reject(new Error(&quot;Element not found&quot;));
      }, timeout);
    }
  });
}
export default waitForElement;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully this will provide you with a basis for a utility of your own.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Observing Element Changes in the Shadow DOM&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>On Being An Advocate</title><link>https://deliciousreverie.co.uk/posts/on-being-an-advocate/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/on-being-an-advocate/</guid><description>I&apos;ve always been interested in psychology but never went further than college with it. Recently a friend introduced me to the Meyers-Briggs Type Indicator Test. It illuminated some important traits of my personality and hilighted the motivation behind the goals I have. I thought I&apos;d go over a few points that interested me here. </description><pubDate>Sat, 23 Jan 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve always been interested in psychology but never went further than college with it. Recently a friend introduced me to the Meyers-Briggs Type Indicator Test. It illuminated some important traits of my personality and hilighted the motivation behind the goals I have. I thought I&apos;d go over a few points that interested me here.&lt;/p&gt;
&lt;p&gt;I studied Carl Jung a bit in college, and always found psychology an absorbing and fascinating field. I was particularly drawn to the practice of counselling, and pursued that for a while before realising that&apos;s not what I really wanted to achieve in life.&lt;/p&gt;
&lt;p&gt;The website &lt;a href=&quot;https://www.16personalities.com/&quot;&gt;Sixteen Personalities&lt;/a&gt; has a scarily accurate test which identifies your type based on the theory that there are 16 main personality types.&lt;/p&gt;
&lt;p&gt;In my test, it was revealed that I am an INFJ personality type — identified on this website as &quot;the Advocate&quot;. So here&apos;s a little bit more about him.&lt;/p&gt;
&lt;h3&gt;&quot;INFJs tend to see helping others as their purpose in life&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22infjs-tend-to-see-helping-others-as-their-purpose-in-life%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This really struck a cord with me. Whether it&apos;s helping to find other developers work, providing training or support for colleagues, or just listening to the personal troubles of my friends, I&apos;m always aiming to help others pursue their goals.&lt;/p&gt;
&lt;p&gt;I try not to be motivated by self-interest and find no satisfaction in sales targets or monetary bonuses. I&apos;d like to be in a position where I can encourage and help others in work and in life generally.&lt;/p&gt;
&lt;h3&gt;&quot;There is a running theme with INFJs, and that is a yearning for authenticity and sincerity – in their activities, their romantic relationships, and their friendships&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22there-is-a-running-theme-with-infjs-and-that-is-a-yearning-for-authenticity-and-sincerity-in-their-activities-their-romantic-relationships-and-their-friendships%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I find I identify quite closely with this statement. I&apos;m always trying to be honest with other people and expect honesty back. At the same time, INFJs have a tendency to be able to spot insincerity and lies. If I found I have been lied to I can react quite badly.&lt;/p&gt;
&lt;h3&gt;&quot;If there’s anything INFJs avoid, it’s the accumulation of power over others – and the people who are drawn to that type of power&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22if-there&apos;s-anything-infjs-avoid-it&apos;s-the-accumulation-of-power-over-others-and-the-people-who-are-drawn-to-that-type-of-power%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I sometimes derail situations when I perceive that I have power over others, and it&apos;s one of the reasons I don&apos;t like to speak publicly even though I have been told I&apos;m a competent speaker :- it puts me on a pedestal and could lead to others investing confidence in me personally, whereas I want others to focus on the goals they have and the tasks they&apos;ve been assigned.&lt;/p&gt;
&lt;p&gt;I have been known to actively undermine the confidence of others in me, so that I don&apos;t feel like I have authority over them.&lt;/p&gt;
&lt;h3&gt;&quot;INFJs crave creativity, the ability to use their insight to connect events and situations, effecting real change in others’ lives personally&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22infjs-crave-creativity-the-ability-to-use-their-insight-to-connect-events-and-situations-effecting-real-change-in-others&apos;-lives-personally%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hmm, how interesting this is in conjunction with my chosen career. I am not content as a coder or a designer, but I want to perceive the connection between the two, as well as the role of content writer and information architect. I have always sought a holistic approach to my career.&lt;/p&gt;
&lt;h3&gt;&quot;INFJs are still Introverts, — they will need to step back and act the lone wolf from time to time, pursuing their own goals in their own ways&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22infjs-are-still-introverts-they-will-need-to-step-back-and-act-the-lone-wolf-from-time-to-time-pursuing-their-own-goals-in-their-own-ways%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This was an interesting revelation. Whilst I feel satisfied in contributing to the goals of others, I still love the times I&apos;m working on personal projects, or writing articles for this blog.&lt;/p&gt;
&lt;h3&gt;&quot;[INFJs] imagination and empathy make [them] someone who not only cherishes their integrity and deeply held principles but, unlike many other idealistic types, is also capable of turning those ideals into plans, and executing them&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#%22infjs-imagination-and-empathy-make-them-someone-who-not-only-cherishes-their-integrity-and-deeply-held-principles-but-unlike-many-other-idealistic-types-is-also-capable-of-turning-those-ideals-into-plans-and-executing-them%22&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It&apos;s always nice to get a bit of a personality massage, but I do have deep-seated principles which I am not prepared to compromise on. Yet when I comprehend the reason these are asked of me, I can usually think of a way around the situation in a mutually beneficial way.&lt;/p&gt;
&lt;h3&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-being-an-advocate/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The trouble with taking this test is that now I want everyone I know to take it so I can discover more about them! There are a few people whom I have no idea where they&apos;re coming from, so I&apos;m scheming for them to take the test at some point in the hopes of understanding them a bit better.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:On Being An Advocate&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>On digital estates</title><link>https://deliciousreverie.co.uk/posts/on-digital-estates/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/on-digital-estates/</guid><description>I recently heard the term &apos;Digital estate&apos; to mean the technical property of an organisation. This, and the associations the term provides, have got me thinking about how we contribute to the success of organisations over time... </description><pubDate>Mon, 29 Nov 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I recently heard the term &apos;Digital estate&apos; to mean the technical property of an organisation. This, and the associations the term provides, have got me thinking about how we contribute to the success of organisations over time...&lt;/p&gt;
&lt;p&gt;Let&apos;s start with this question: what does the word &quot;estate&quot; conjure up in your mind? I think it might include a variety of things. A house? Probably? The physical things you own? Maybe.&lt;/p&gt;
&lt;p&gt;I live in a country where that term can have two very distinct meanings beyond those two things.&lt;/p&gt;
&lt;p&gt;In times gone by, the ruling elite would have an estate. It usually consisted of one major, very large property that resembled (or actually was) a castle or manor house. It would also include extensive land around that area, much of it farmland. It could also include villages where people often worked for the estate. It was like the country&apos;s economy in miniature.&lt;/p&gt;
&lt;p&gt;If you&apos;ve watched Downton Abbey, you probably know what I&apos;m getting at here.&lt;/p&gt;
&lt;p&gt;Then ... there&apos;s the council estate.&lt;/p&gt;
&lt;p&gt;In this context, an estate is a collection of houses owned by a local council where people of less secure financial means can find accommodation. Although valiant attempts have often been made to increase the opportunities for people who live in these kinds of places, there&apos;s often still a lot of crime and violence. For many people it&apos;s home, and therefore it&apos;s a nice place to live. But given the opportunity, would they choose to live elsewhere?&lt;/p&gt;
&lt;p&gt;So what kind of &quot;estate&quot; are we talking about when it comes to a digitalestate?&lt;/p&gt;
&lt;p&gt;I guess it could be either really.&lt;/p&gt;
&lt;h2&gt;Be mindful &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-digital-estates/#be-mindful&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What I&apos;m saying is that we should be mindful of what we&apos;re actually building. The code we write on a daily basis contributes to the overall estate of the organisation we&apos;re working for. Are we making that estate into something that is easy to move around in, neat, tidy, something we can be proud of? Or is it something that serves a necessary function but is difficult to work with?&lt;/p&gt;
&lt;p&gt;It depends on our attitude towards our work ... are we tempted to think that, because our surroundings are not conducive to finishing things off nicely, that we should do a sloppy job? Or do we have the opinion that we should try to improve the quality of life regardless of the situation of those around us?&lt;/p&gt;
&lt;h2&gt;The organisations view &lt;a href=&quot;https://deliciousreverie.co.uk/posts/on-digital-estates/#the-organisations-view&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The overall organisation has a huge impact on the digital estate. Sometimes in ways they might not imagine. For example, technical debt is likely to always exist. But are teams encouraged to tackle this debt? Or does the organisation skirt around the issue, thinking that they can maintain velocity without paying it off?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.slideshare.net/AgileVelocity1/technical-debt-sources-and-impacts-57655575&quot;&gt;for more on this subject, please see this slideshare&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The organisation might want to keep producing features under pressure from stakeholders or higher management. Or they might make decisions about launch timelines of new digital products without asking the opinion of those who understand the cost of delivery.&lt;/p&gt;
&lt;p&gt;Both of these increase the rate of technical debt. So &quot;shipping faster&quot; is counterintuitive to the ultimate velocity of the organisation. On this subject, I really enjoyed &lt;a href=&quot;https://www.infoq.com/articles/reduce-technical-debt/&quot;&gt;this article on the InfoQ website&lt;/a&gt;, which claims that &quot;In the future, technical debt will become less of an engineering problem and more of an important business prerequisite that helps with delivering more value to our customers and the business.&quot;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:On digital estates&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>From PHP to JavaScript: How I Switched Stacks</title><link>https://deliciousreverie.co.uk/posts/php-to-javascript-how-i-switched-stacks/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/php-to-javascript-how-i-switched-stacks/</guid><description>I&apos;ve been working in web development for the past 8 years or so. Traditionally, I have worked at agencies, creating sites that have been designed in Photoshop, and built afterwards with a CMS integration such as Perch, WordPress, or similar, with occasional forays into Laravel. </description><pubDate>Mon, 08 Aug 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve been working in web development for the past 8 years or so. Traditionally, I have worked at agencies, creating sites that have been designed in Photoshop, and built afterwards with a CMS integration such as Perch, WordPress, or similar, with occasional forays into Laravel.&lt;/p&gt;
&lt;p&gt;During that time I&apos;ve had the opportunity to work with JavaScript too. But understanding the language well seemed a bit beyond my grasp. One of the reasons for this was, I think, that I was trying to juggle several languages at once. I spent a lot of time understanding design patterns in PHP. Then, after I interviewed at Google, I decided to focus more deeply on CSS and HTML. And with these PHP projects, a lot was done in that language, leaving JavaScript as a vehicle for presentational enhancement only.&lt;/p&gt;
&lt;p&gt;But then, because of concerns about WordPress&apos; future, the agency I was working for invested time for its developers into finding a possible alternative to that popular platform. Lead developer Chris Geary saw an opportunity here. Instead of trying to replace WordPress with another platform in a 1:1 comparison, he realised we could deliver serverless sites using some cutting-edge tools that were emerging.&lt;/p&gt;
&lt;h2&gt;Investigating Serverless &lt;a href=&quot;https://deliciousreverie.co.uk/posts/php-to-javascript-how-i-switched-stacks/#investigating-serverless&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This approach had the advantage of allowing a transition: the agency could keep selling and building WordPress sites (they&apos;d been doing this for 10 years and were comfortable with it), but these sites would have a separate front-end using the headless CMS strategy. It also allowed them a marketing opportunity: headless was a buzzword, they could position themselves as a thought leader and gain reputation from that.&lt;/p&gt;
&lt;p&gt;So began my journey to serverless. I was introduced to Netlify, and promptly moved my sites from PHP to static, which allowed me to see the benefits of continuous integration, and saving myself a monthly hosting fee in the process.&lt;/p&gt;
&lt;p&gt;I experimented with Jekyll and Hugo, but I didn&apos;t see a future for me in either of these languages (Ruby and Go, respectively).&lt;/p&gt;
&lt;p&gt;Then Chris started talking about Gatsbyjs. So I built a Gatsby site, &lt;a href=&quot;http://freebabylon5.com/&quot;&gt;FreeBabylon5.com&lt;/a&gt;. As mentioned, I hadn&apos;t done a huge amount of heavy lifting in JavaScript before. I&apos;d only toyed with React. I had no idea what I was doing and that felt awesome.&lt;/p&gt;
&lt;p&gt;As I learned Gatsbyjs I noticed a typo in the docs and submitted an issue. I was invited to fix the issue myself and submit a pull request. I had never done anything like this before! Cloning an open-source project, branching off so I could fix the issue, pushing up my code and submitting a Pull Request ... wow. With this new knowledge I felt like I was finally able to give back to some of the amazing tools I&apos;d so often used in the past.&lt;/p&gt;
&lt;h2&gt;Documenting the Experience &lt;a href=&quot;https://deliciousreverie.co.uk/posts/php-to-javascript-how-i-switched-stacks/#documenting-the-experience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My personal view is that I should document everything I learn, in the hopes that someone else might be able to benefit from my experience. So I started writing about my explorations here on delicious reverie.&lt;/p&gt;
&lt;p&gt;To my amazement the creators of Gatsbyjs read my post and asked me to publish it on the Gatsbyjs blog. This gave me more confidence to progress further along this path, invest in my own learning towards JavaScript and using Gatsbyjs for more projects.&lt;/p&gt;
&lt;p&gt;Around the same time, my colleagues and I became more concerned for developers using WordPress. It seemed that many of them could soon be pushed out of that platform for reasons I&apos;ll explain in another post. We wanted to help them identify another route that would also encourage best practices in web development.&lt;/p&gt;
&lt;p&gt;So I submitted a talk outline for WordCamp London, which is usually attended by 400-500 developers. I also approached print magazine Net on the same subject. Both the talk and the article were accepted. I soon found myself on the main stage at the conference delivering a 40-minute talk, and saw my material published in the print magazine.&lt;/p&gt;
&lt;p&gt;I get so terrified about pushing myself forward in these ways, especially because I was new at all of this. But I felt I had something that could potentially help some other people. That motivated me to overcome my abject fear.&lt;/p&gt;
&lt;h2&gt;Zopa&apos;s Serverless Initiative &lt;a href=&quot;https://deliciousreverie.co.uk/posts/php-to-javascript-how-i-switched-stacks/#zopa&apos;s-serverless-initiative&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Around this time peer-to-peer lender Zopa were looking to replace a Ruby app with a static website. So they approached me directly and called me for several weeks until I finally had time to hear what they were trying to say.&lt;/p&gt;
&lt;p&gt;Zopa had already identified Gatsbyjs as their tool of choice because they had lots of in-house JavaScript and React expertise. They had partnered with an agency to design and build the site, and wanted me on board to help develop it because of my experience with the framework. I&apos;ll be able to tell you more about the site when it launches, but it&apos;s been a great experience working with some excellent and incredibly smart people both at the agency and at Zopa.&lt;/p&gt;
&lt;p&gt;I&apos;ve had to learn a lot in a short space of time, but Zopa have been supportive and patient with my learning experience. In return, I&apos;ve been able to help identify where Gatsby&apos;s APIs can be used to great effect instead of a custom solution that renders content on the frontend. I&apos;ve been able to help other developers explore the GraphQL syntax and use Gatsbyjs&apos;s built-in IDE to query the data structure. And I&apos;ve helped identify where opportunities to use functions hosted on other platforms enable us improve performance.&lt;/p&gt;
&lt;p&gt;What I&apos;ve most enjoyed is seeing those &quot;wow&quot; moments as developers really get GatsbyJS, and how, because of it&apos;s approach to data, it&apos;s not just a static site generator, it&apos;s much more than that.&lt;/p&gt;
&lt;p&gt;So now, instead of working with agencies on predominantly PHP projects I&apos;m a JavaScript dev working in central London. The deadlines are just as tight, the pressure to deliver can sometimes be much more, and learning a complicated custom-built infrastructure has been a challenge. But I&apos;ve really enjoyed experiencing things from this side of the fence.&lt;/p&gt;
&lt;p&gt;I&apos;m continuing to work with internal teams at Zopa to give them the opportunity to contribute to the project. I&apos;m really hoping I can do more with GatsbyJS as a contributor. And I&apos;m glad to say that in spite of missing the close-knit community of developers I&apos;ve been around up until now, I&apos;m happier being focused on JavaScript than I&apos;ve ever been.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:From PHP to JavaScript: How I Switched Stacks&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Poem Review: She Weeps Over Rahoon, James Joyce</title><link>https://deliciousreverie.co.uk/posts/poem-review-she-weeps-over-rahoon-james-joyce/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/poem-review-she-weeps-over-rahoon-james-joyce/</guid><description>A very dear friend introduced me to James Joyce many years ago with his coming-of-age almost-biography, A Portrait of a Young Man, which had a deep impact on me. But it&apos;s only now I&apos;m starting to pick up on just what a master of the English language Joyce was—through his poetry. </description><pubDate>Mon, 21 Nov 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;A very dear friend introduced me to James Joyce many years ago with his coming-of-age almost-biography, A Portrait of a Young Man, which had a deep impact on me. But it&apos;s only now I&apos;m starting to pick up on just what a master of the English language Joyce was—through his poetry.&lt;/p&gt;
&lt;p&gt;Joyce for many people, I am sad to say it, might always be an inaccessible bastion of 20th century literature. It&apos;s true that I&apos;ve struggled and failed to maintain enthusiasm for, much less read Ulysses and Finnegan&apos;s Wake, as highly regarded as they are.&lt;/p&gt;
&lt;p&gt;But Joyce&apos;s power over the English language was demonstrated ever so well in his poems, too, and it&apos;s these that I treasure above his larger volumes. If you&apos;re not convinced, spend a few hours reading chamber music carefully. It&apos;s truly beautiful and evocative.&lt;/p&gt;
&lt;p&gt;Yet, until very recently, I had totally missed out on this shorter poem:&lt;/p&gt;
&lt;h3&gt;She Weeps Over Rahoon &lt;a href=&quot;https://deliciousreverie.co.uk/posts/poem-review-she-weeps-over-rahoon-james-joyce/#she-weeps-over-rahoon&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rain on Rahoon falls softly, softly falling,&lt;/p&gt;
&lt;p&gt;Where my dark lover lies.&lt;/p&gt;
&lt;p&gt;Sad is his voice that calls me, sadly calling,&lt;/p&gt;
&lt;p&gt;At grey moonrise.&lt;/p&gt;
&lt;p&gt;Love, hear thou&lt;/p&gt;
&lt;p&gt;How soft, how sad his voice is ever calling,&lt;/p&gt;
&lt;p&gt;Ever unanswered, and the dark rain falling,&lt;/p&gt;
&lt;p&gt;Then as now.&lt;/p&gt;
&lt;p&gt;Dark too our hearts, O love, shall lie and cold&lt;/p&gt;
&lt;p&gt;As his sad heart has lain&lt;/p&gt;
&lt;p&gt;Under the moongrey nettles, the black mould&lt;/p&gt;
&lt;p&gt;And muttering rain.&lt;/p&gt;
&lt;p&gt;There is some biographical background to this poem, which is that Joyce&apos;s lover was visiting the grave of a previous love, which inspired Joyce to write the poem. Doesn&apos;t it evoke some of the poignant sadness of that occasion, as well as some of the other sad fates of unmentioned Irish men?&lt;/p&gt;
&lt;p&gt;But as with many 20th century authors, although the context is interesting, the text is much more than a sum of its parts. For me the cadence of the poem as well as its structure conjures up the wind, rain and bleakness of Rahoon. But just enough of it is obscure that I start to ruminate over what I don&apos;t know, or isn&apos;t explicitly stated.&lt;/p&gt;
&lt;p&gt;For instance, why is it Rahoon she weeps over, not her lover? Is there some greater tragedy below the surface? The line dark too our hearts makes me think about my own mortality too, which brings the tragedy home with a great sharpness.&lt;/p&gt;
&lt;p&gt;Joyce was an absolute master of the English language, and seemed to know just what to say that would reach through the pages into the readers&apos; heart.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Poem Review: She Weeps Over Rahoon, James Joyce&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Poems about Time</title><link>https://deliciousreverie.co.uk/posts/poems-about-time/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/poems-about-time/</guid><description>Time is a funny thing. Whilst modern science has helped us to see it&apos;s not as constant as we might have imagined, we still rarely are able to see it and our experiences within it from different perspectives. Although I really enjoy these few poems that seem to be able to step outside of the flow in one way or another.</description><pubDate>Mon, 02 Jan 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Time is a funny thing. Whilst modern science has helped us to see it&apos;s not as constant as we might have imagined, we still rarely are able to see it and our experiences within it from different perspectives. Although I really enjoy these few poems that seem to be able to step outside of the flow in one way or another.&lt;/p&gt;
&lt;h2&gt;Clouds - Joni Mitchell&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why I like it:&lt;/strong&gt; Joni has an amazing mind and all of her music shares the immediacy and intimacy of a life of experiences. Yet this one in particular is written from three unique perspectives: the first verse is Joni as a child looking at clouds and noticing, perhaps for the first time, the passing of time. As the verses progress, she gets older and reaches adulthood and then old age, and has time to reflect on the similarities between clouds, and love, and indeed life itself&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Rows and flows of angel hair
And ice cream castles in the air
And feather canyons everywhere
Looked at clouds that way
But now they only block the sun
They rain and they snow on everyone
So many things I would have done
But clouds got in my way
I&apos;ve looked at clouds from both sides now
From up and down and still somehow
It&apos;s cloud illusions I recall
I really don&apos;t know clouds at all
Moons and Junes and Ferris wheels
The dizzy dancing way that you feel
As every fairy tale comes real
I&apos;ve looked at love that way
But now it&apos;s just another show
And you leave &apos;em laughing when you go
And if you care, don&apos;t let them know
Don&apos;t give yourself away
I&apos;ve looked at love from both sides now
From give and take and still somehow
It&apos;s love&apos;s illusions that I recall
I really don&apos;t know love 
Really don&apos;t know love at all
Tears and fears and feeling proud
To say, &quot;I love you&quot; right out loud
Dreams and schemes and circus crowds
I&apos;ve looked at life that way
Oh, but now old friends they&apos;re acting strange
And they shake their heads and they tell me that I&apos;ve changed
Well something&apos;s lost, but something&apos;s gained
In living every day
I&apos;ve looked at life from both sides now
From win and lose and still somehow
It&apos;s life&apos;s illusions I recall
I really don&apos;t know life at all
It&apos;s life&apos;s illusions that I recall
I really don&apos;t know life
I really don&apos;t know life at all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Time - Pink Floyd (David Gilmour, Nick Mason,  Roger Waters and Richard Wright)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why I like it:&lt;/strong&gt; This is one of the songs I grew up with: my dad played it for me when I was a teen. It&apos;s bound to a young mans&apos; perspective but is remarkably foresighted; the writer already perceives that life will go by quickly and will inevitably end in regret one way or another.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Ticking away the moments that make up a dull day
Fritter and waste the hours in an offhand way
Kicking around on a piece of ground in your hometown
Waiting for someone or something to show you the way

Tired of lying in the sunshine, staying home to watch the rain
You are young and life is long, and there is time to kill today
And then one day you find ten years have got behind you
No one told you when to run, you missed the starting gun

And you run, and you run to catch up with the sun but it&apos;s sinking
Racing around to come up behind you again
The sun is the same in a relative way but you&apos;re older
Shorter of breath and one day closer to death
Every year is getting shorter, never seem to find the time
Plans that either come to naught or half a page of scribbled lines

Hanging on in quiet desperation is the English way
The time is gone, the song is over, thought I&apos;d something more to say
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Shakespeare - Sonnet 19​&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why I like it:&lt;/strong&gt; Maybe Dylan Thomas was thinking of this sonnet when he was writing &quot;do not go gentle&quot;, another poem I admire but for different reasons. In this poem, the protagonist (it could be Shakespeare; but he hardly ever wrote from his own perspective) rages against time, almost sticking a thumb up, because he believes he has found a way to outwit time: by writing this very verse.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Devouring Time,  blunt thou the lion’s paws,
And make the earth devour her own sweet brood;
Pluck the keen teeth from the fierce tiger’s jaws,
And burn the long-liv’d phœnix in her blood;
Make glad and sorry seasons as thou fleets,
And do whate’er thou wilt, swift-footed Time,
To the wide world and all her fading sweets;
But I forbid thee one most heinous crime:
O, carve not with thy hours my love’s fair brow,
Nor draw no lines there with thine antique pen;
Him in thy course untainted do allow
For beauty’s pattern to succeeding men.
Yet do thy worst, old Time: despite thy wrong,
My love shall in my verse ever live young.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Keats - Ode on a Grecian Urn&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Why I like it:&lt;/strong&gt; This ode remains my favourite verse on the subject of time. In it, time is frozen in one single moment by the creation of art. It&apos;s almost a response to Sonnet 19 above, where someone future is able to appreciate that time is beaten; but Keats&apos; luscious and very living words betray a sad fact: the impression is real, but the people, as Keats himself, are gone.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Thou still unravish&apos;d bride of quietness,
  Thou foster-child of silence and slow time,
Sylvan historian, who canst thus express
  A flowery tale more sweetly than our rhyme:
What leaf-fring&apos;d legend haunts about thy shape 
Of deities or mortals, or of both,
    In Tempe or the dales of Arcady?
  What men or gods are these? What maidens loth?
What mad pursuit? What struggle to escape?
    What pipes and timbrels? What wild ecstasy?

Heard melodies are sweet, but those unheard
  Are sweeter; therefore, ye soft pipes, play on;
Not to the sensual ear, but, more endear&apos;d,
  Pipe to the spirit ditties of no tone:
Fair youth, beneath the trees, thou canst not leave
  Thy song, nor ever can those trees be bare;

    Bold Lover, never, never canst thou kiss,
Though winning near the goal yet, do not grieve;
  She cannot fade, though thou hast not thy bliss,
    For ever wilt thou love, and she be fair!
Ah, happy, happy boughs! that cannot shed
      Your leaves, nor ever bid the Spring adieu;
And, happy melodist, unwearied,
      For ever piping songs for ever new;

More happy love! more happy, happy love!
      For ever warm and still to be enjoy&apos;d,
       For ever panting, and for ever young;
All breathing human passion far above,
      That leaves a heart high-sorrowful and cloy&apos;d,
       A burning forehead, and a parching tongue.

Who are these coming to the sacrifice?
      To what green altar, O mysterious priest,
Lead&apos;st thou that heifer lowing at the skies,
      And all her silken flanks with garlands drest?

What little town by river or sea shore,
      Or mountain-built with peaceful citadel,
       Is emptied of this folk, this pious morn?
And, little town, thy streets for evermore
      Will silent be; and not a soul to tell
       Why thou art desolate, can e&apos;er return.
       
O Attic shape! Fair attitude! with brede
      Of marble men and maidens overwrought,
With forest branches and the trodden weed;
      Thou, silent form, dost tease us out of thought
As doth eternity: Cold Pastoral!
      When old age shall this generation waste,
       Thou shalt remain, in midst of other woe
Than ours, a friend to man, to whom thou say&apos;st,
      &quot;Beauty is truth, truth beauty,—that is all
       Ye know on earth, and all ye need to know.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;I find time fascinating because its so intrinsic to our nature as humans; if we were to not exist within the stream of time we would hardly be called such. Yet I can also appreciate that there exists a perspective that is outside its bounds.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Poems about Time&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Polymorphic Elements in Astro</title><link>https://deliciousreverie.co.uk/posts/polymorphic-elements-astro/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/polymorphic-elements-astro/</guid><description>I&apos;m building a component library in Astro, and one of the things I needed to do was to build a component that could render as either a button or an anchor tag. Here&apos;s how I achieved that.</description><pubDate>Wed, 22 Feb 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m building a component library in Astro, and one of the things I needed to do was to build a component that could render as either a button or an anchor tag. Here&apos;s an example of the outcome I wanted to achieve:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
 // renders an &amp;lt;a&amp;gt; tag
&amp;lt;Button href=&quot;https://some-link&quot;&amp;gt;This is an anchor tag&amp;lt;/Button&amp;gt;
 // renders a &amp;lt;button&amp;gt; tag
&amp;lt;Button type=&quot;button&quot;&amp;gt;Submit&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With Astro you&apos;re writing the best parts of JSX there could be;  for example, there are no abstraction leaks like having to add `key`s to iterables. But also what you&apos;re writing is very obviously much closer to HTML than JSX is, there not being a virtual DOM, or styled components, so this puzzled me for a while. I could either have 2 components and duplicate my styles, or find a way to render either depending on the use case.&lt;/p&gt;
&lt;p&gt;Thankfully with some clever prop handling, we can have the best of both worlds:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// button.astro

const { href } = Astro.props

---
&amp;lt;&amp;gt;
  {
    href ? (
      &amp;lt;a href={href}&amp;gt;&amp;lt;slot /&amp;gt;&amp;lt;/a&amp;gt;
    ) : (
      &amp;lt;button
        type=&quot;button&quot;
      &amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/button&amp;gt;
    )
  }
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this code, the default output is a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element, but if you pass a &lt;code&gt;href&lt;/code&gt;, it will instead render an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag. What I love about this is that we are also using another trick first implemented by React, the Fragment. By wrapping the component in fragments (empty tags, or &lt;code&gt;&amp;lt;&amp;gt; &amp;lt;/&amp;gt;&lt;/code&gt;), you&apos;re still returning one element, which is a requirement for Astro components, but eliminating the wrapper in the compiled code. This way you don&apos;t have an extra `div` which could cause styling issues later on.&lt;/p&gt;
&lt;p&gt;But what if you wanted more polymorphic elements? Say you had a container that could sometimes be a section, and sometimes you&apos;d want to render it as a plain ol&apos; div element. Here&apos;s a solution for that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
interface Props {
  as?: &quot;section&quot;;
}

const { as } = Astro.props;

---

&amp;lt;&amp;gt;
  {as === &apos;section&apos; &amp;amp;&amp;amp; (
      &amp;lt;section&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/section&amp;gt;
  )}
  {!as &amp;amp;&amp;amp; (
    &amp;lt;div&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/div&amp;gt;
  )}
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By specifying the types, we give whoever is using this component the hints to show them what elements they can render this component to.&lt;/p&gt;
&lt;p&gt;It&apos;s nicely extensible too: if you wanted to have another tag, say an &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;, you could just add that to the types and to the return:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;&amp;gt;
  {as === &apos;aside&apos; &amp;amp;&amp;amp; (
      &amp;lt;aside&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/aside&amp;gt;
  )}
  {as === &apos;section&apos; &amp;amp;&amp;amp; (
      &amp;lt;section class:list={classList}&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/section&amp;gt;
  )}
  {!as &amp;amp;&amp;amp; (
    &amp;lt;div&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/div&amp;gt;
  )}
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I really like this because the person using this code doesn&apos;t have to worry about polymorphism at all, the default return is a div, which they don&apos;t have to specify. But what about passing styles to all of these different elements?&lt;/p&gt;
&lt;h2&gt;Styling polymorphic elements&lt;/h2&gt;
&lt;p&gt;For my component I wanted to pass in a default set of Tailwind classes, but also allow the person using the component to be able to override them by passing in an arbitrary string. So I used Astro&apos;s class:list helper:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const { overrideClasses, as } = Astro.props;

const classList = new Set([
  &quot;tw-mx-auto tw-max-w-7xl tw-px-2 sm:px-6 tw-lg:px-8&quot;,
  overrideClasses,
]);
---
&amp;lt;&amp;gt;
  {as === &apos;section&apos; &amp;amp;&amp;amp; (
      &amp;lt;section class:list={classList}&amp;gt;
        &amp;lt;slot /&amp;gt;
      &amp;lt;/section&amp;gt;
  )}
  {!as &amp;amp;&amp;amp; (
    &amp;lt;div class:list={classList}&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/div&amp;gt;
  )}
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using polymorphic elements I&apos;m keeping code dry, making it reusable, and providing the best possible experience for my colleagues.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Polymorphic Elements in Astro&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Project Documentation Templates</title><link>https://deliciousreverie.co.uk/posts/project-documentation-template/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/project-documentation-template/</guid><description>Writing documentation for new team members - or just to provide a referenceable standard - is hard. I&apos;ve provided these documents with the aim of helping this process. </description><pubDate>Sun, 01 Nov 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Writing documentation for new team members - or just to provide a referenceable standard - is hard. I&apos;ve provided these documents with the aim of helping this process.&lt;/p&gt;
&lt;p&gt;I&apos;ll be honest, I&apos;m not naturally an organised person. But I think that gives me an advantage. It means I have to try harder to be organised. And because I hate the chaos that can result, I&apos;m pretty determined to get things organised and keep them that way.&lt;/p&gt;
&lt;h3&gt;Consequences &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#consequences&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I suppose I don&apos;t really have to revisit what happens when a project is disorganised, do I? We&apos;ve all been there, and have perhaps been guilty of it ourselves at one time or another. But it has some pretty drastic effects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Loss of profit&lt;/li&gt;
&lt;li&gt;Developer disillusionment&lt;/li&gt;
&lt;li&gt;Project can fail to meet client expectations&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All three of these can have a really bad effect on the atmosphere in any team or organisation.&lt;/p&gt;
&lt;h3&gt;Help is Available! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#help-is-available!&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;ve just set up a repo of documents that I helped develop whilst I was at Blaze Communication. These practices allowed us to increase profitability, better meet client expectations, and they certainly helped developers feel more informed, empowered and invested in their projects.&lt;/p&gt;
&lt;h4&gt;Contractor Guidelines &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#contractor-guidelines&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The Contractor Guidelines document sets out some principles for contractors. It was chiefly written for off-site developers. I have tried to avoid stipulating rules where possible but set down some best practices — &quot;guidelines&quot; that developers can hopefully use to work smarter instead of harder.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/endymion1818/team-documentation/blob/master/contractor-guidelines.md&quot;&gt;GitHub Link to Contractor Guidelines&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Prelaunch Checklist &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#prelaunch-checklist&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;It&apos;s possibly the worst feeling in the world when a project has gone live and you realise you missed that key security feature, or forgot to activate this piece of functionality. It&apos;s even worse to get a call from the client several weeks down the line asking for analytics data ... then realising you forgot to add the code snippet.&lt;/p&gt;
&lt;p&gt;This Prelaunch Checklist, from an original by friend and colleague &lt;a href=&quot;https://maltpress.co.uk/&quot;&gt;Adam Maltpress&lt;/a&gt; can be used as a template for your projects.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/endymion1818/team-documentation/blob/master/prelaunch-checklist.md&quot;&gt;GitHb Link to Prelaunch Checklist&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Technical Manual &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#technical-manual&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When I was leaving Blaze I wanted to leave behind more knowledge to the replacement developer than I had the opportunity to say in a handover meeting. This &quot;Technical Manual&quot; sets out some broad strokes for a new developer, and can be used as a handbook for new developers to refer to.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/endymion1818/team-documentation/blob/master/technicalmanual.md&quot;&gt;GitHb Link to Technical Manual&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Project Scope &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#project-scope&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This is the most barebones of templates, and shouldn&apos;t be too extensive. It should only outline some key deliverables, specifications and user stories that have been discussed and agreed by the client and the development team. As well as affording deniability, this can help give more direction and focus to a project.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/endymion1818/team-documentation/blob/master/projectscope.md&quot;&gt;GitHb Link to Project Scope&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Missing Stuff? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/project-documentation-template/#missing-stuff&quot;&gt;#&lt;/a&gt;&quot;&lt;/h3&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Project Documentation Templates&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Publishing both JS and non-JS sites</title><link>https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/</guid><description>It&apos;s always bothered me that the majority of internet users spend a lot of money downloading and running JavaScript, yet I enjoy building things with JavaScript, and want to provide an enhanced experience using JS ... without sacrificing their needs to do so. Now, there&apos;s an easier way to do both. </description><pubDate>Fri, 18 Sep 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;It&apos;s always bothered me that the majority of internet users spend a lot of money downloading and running JavaScript, yet I enjoy building things with JavaScript, and want to provide an enhanced experience using JS without sacrificing their needs to do so. Here&apos;s one way we can have the best of both worlds.&lt;/p&gt;
&lt;p&gt;I&apos;m not going to argue JS vs no JS use here, I don&apos;t think it&apos;s even an argument. In my opinion we need to provide the best experience we can for all of our users, and that includes both those on slow connections and those on fast ones.&lt;/p&gt;
&lt;p&gt;Now, there&apos;s an easier way to do both.&lt;/p&gt;
&lt;h2&gt;Why this site? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#why-this-site&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I don&apos;t really need any JavaScript on this site. But I used GatsbyJS to build it, which by generates HTML but then overlays that with a React app (I already switched from React to Preact for better performance benefits). I had a site search and an animation, which wasn&apos;t part of the core experience but were nice enhancements.&lt;/p&gt;
&lt;p&gt;So I decided that I should by default provide an experience which was friendlier to those with CPU or battery restrictions, and then have another domain which had the fancy stuff.&lt;/p&gt;
&lt;h2&gt;Setup using Gatsby Plugins &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#setup-using-gatsby-plugins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used two plugins to render an HTML &amp;amp; CSS site with Gatsby: gatsby-plugin-no-javascript and gatsby-plugin-no-javascript-utils. The first builds the site as normal, but then unlinks the JavaScript, so that all you get is the HTML version of the site that Gatsby already builds. The second allows you to do some other finessing so that you can disable inline styles and remove sourcemaps too.&lt;/p&gt;
&lt;p&gt;After installing and configuring these plugins, the next step was to setup an environment variable:&lt;/p&gt;
&lt;p&gt;In a file called &lt;strong&gt;.env&lt;/strong&gt; in the root of the project, I added:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;JS_DISABLED=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then in my gatsby-config.js I access this variable by adding the following to the top of the file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require(&quot;dotenv&quot;).config()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, I wanted to keep everything else about the configurations the same. Since the export in this file is a single JavaScript object, I could compose it up from separate elements. I could have one array for the plugins, another array for my noJS plugins, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const defaultPlugins = [
  // everything else
];

const noJsPlugins = [
  ...defaultPlugins,
  `gatsby-plugin-no-javascript`,
  {
    resolve: `gatsby-plugin-no-javascript-utils`,
    options: {
      removeGeneratorTag: false,
      noInlineStyles: true,
    },
  },
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now combine them into one object, conditionally choosing which array of plugins to choose, and export it as a module:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module.exports = {
  siteMetadata: {
    pathPrefix: &quot;/&quot;,
    title: siteTitle,
    siteUrl: `https://deliciousreverie.co.uk`,
    description: `blog of developer &amp;amp; bookworm benjamin read`,
  },
  plugins:
    process.env.JS_DISABLED === &quot;true&quot; ? [...noJsPlugins] : [...defaultPlugins],
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Setting up on Netlify &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#setting-up-on-netlify&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Netlify is very smart. I only needed to do a few things: I initiated a new site and chose the same base repo. I had to make sure I was using Netlify&apos;s DNS so that I could use my subdomain with the new site. Then I set the environment variables on both sites, merged the code and it was live!&lt;/p&gt;
&lt;h2&gt;Updating the UI &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#updating-the-ui&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I checked the noJS version of my site, the search bar was still visible, although it didn&apos;t work. So I had to stop it from showing on the noJS version.&lt;/p&gt;
&lt;p&gt;I tried using &lt;code&gt;process.env.JS_DISABLED&lt;/code&gt; in the JSX, but Gatsby separates environment variables. If I wanted to access this variable on the frontend, I could rename it &lt;code&gt;GATSBY_JS_DISABLED&lt;/code&gt;, but I thought of another way of doing it that proved just as effective...&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{typeof window !== &apos;undefined&apos; &amp;amp;&amp;amp; &amp;lt;SearchForm /&amp;gt;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, the JSX that gets built on the server doesn&apos;t render the searchform because there&apos;s no Window object. &lt;a href=&quot;https://joshwcomeau.com/react/the-perils-of-rehydration/&quot;&gt;Josh W Comeau has a more robust way of handling this use case on his blog post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I used this again to display a message in the footer to let users know which version of the site they are on:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  typeof window === &quot;undefined&quot; ? (
    &amp;lt;p&amp;gt;
      You&apos;re currently on the &amp;lt;i&amp;gt;javascript disabled&amp;lt;/i&amp;gt; version of the site. To
      enable the site search and some pretty animations,{&quot; &quot;}
      &amp;lt;a href=&quot;https://jsenabled.deliciousreverie.co.uk&quot;&amp;gt;
        view the javascript enabled react app
      &amp;lt;/a&amp;gt;
      .
    &amp;lt;/p&amp;gt;
  ) : (
    &amp;lt;p&amp;gt;
      You&apos;re currently on the &amp;lt;i&amp;gt;javascript enabled&amp;lt;/i&amp;gt; version of the site. if
      you need to conserve your battery or CPU,{&quot; &quot;}
      &amp;lt;a href=&quot;https://deliciousreverie.co.uk&quot;&amp;gt;view the HTML &amp;amp; CSS only site&amp;lt;/a&amp;gt;
      .
    &amp;lt;/p&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I used this method because I wanted to toggle between elements that were inside a single parent element.&lt;/p&gt;
&lt;h2&gt;Discouraging search engines &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#discouraging-search-engines&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finally, I needed to discourage robots from indexing the JS enabled version of the site: the last thing I need is duplicate content warnings or to hurt my own SEO.&lt;/p&gt;
&lt;p&gt;I could have used the same trick I&apos;ve already used above to add this meta tag to the Layout file, but I opted instead to use Netlify&apos;s postprocessing tool to add it to the appropriate site:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta name=&quot;robots&quot; content=&quot;noindex&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I thought this would be more robust since the first pass robots make is done without JavaScript enabled, so it needed to be added to the pre-rendered HTML that&apos;s under the React app.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What&apos;s stopping us building everything like this? It didn&apos;t take long to set up, and with some UI tweaks I got the best of both worlds: a fancy site with all the whistles and bangs of a JavaScript app, and a barebones (but still pretty) site that doesn&apos;t cost the earth to run.&lt;/p&gt;
&lt;h2&gt;Post-Script: &quot;This should be done by [browser, hosting platform, a serverless function]&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#post-script:-%22this-should-be-done-by-browser-hosting-platform-a-serverless-function%22&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the things I hear often is that we developers shouldn&apos;t be responsible for this kind of thing, that it should be the responsibility of some other part of the infrastructure of the web to handle request loads, but I don&apos;t think that&apos;s the case.&lt;/p&gt;
&lt;p&gt;Here&apos;s a few reasons I don&apos;t think it can be the responsibility any of those things:&lt;/p&gt;
&lt;h3&gt;Browser? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#browser&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Browsers start fetching small packets of information, then scale up until they&apos;ve expended the bandwidth. It&apos;s only once they reach that point that they can tell what the users&apos; connection is like. This is probably too late to decide what to send down the wire.&lt;/p&gt;
&lt;h3&gt;Hosting platform? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#hosting-platform&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hosting platforms don&apos;t have the information required to know in detail what the users&apos; connection is like. At best this is an approximation. Could we implement an API so that we can get that data? Maybe, but take a look at what happened with the Battery status API. We don&apos;t want to go there again.&lt;/p&gt;
&lt;h3&gt;Serverless function? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#serverless-function&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hmm ... maybe ... but see above. Potentially an edge function could do it ... though I don&apos;t know enough about this to see how.&lt;/p&gt;
&lt;h3&gt;Nobody? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/publishing-both-js-non-js-sites/#nobody&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Erm no. Someone has to take responsibility here, and it&apos;s not the user. Since we&apos;ve exhausted all other options, I think that we must rise to the challenge and provide people the online experience ... not that they need, but that they deserve.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Publishing both JS and non-JS sites&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Publishing Poetry with Webiny CMS and Astro</title><link>https://deliciousreverie.co.uk/posts/publishing-poetry-with-webiny-cms-astro/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/publishing-poetry-with-webiny-cms-astro/</guid><description>I&apos;ve wanted to have the ability on my blog to render poetry in a way that was conducive to reading, and I think I&apos;ve finally managed to achieve that with my current tech stack, and some repurposing of tools I already use.</description><pubDate>Fri, 10 Mar 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Publishing poetry on the web is ... tricky.&lt;/p&gt;
&lt;p&gt;I admit, some poetry is relatively straightforward: it&apos;s organised similarly to normal text, where on the left side, everything lines up nicely to the left, and right lines are &quot;ragged&quot;, or not justified to any alignment. This is quite commonly how paragraphs appear.&lt;/p&gt;
&lt;p&gt;Some poems, and some poets, choose to organise their work in more ... unusual ways. Take Keat&apos;s Ode on a Grecian Urn, which has meaningful indentations which I think really helps you to appreciate the lyrical cadence of the poem:&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Thou still unravish&apos;d bride of quietness,
       Thou foster-child of silence and slow time,
Sylvan historian, who canst thus express
       A flowery tale more sweetly than our rhyme:
What leaf-fring&apos;d legend haunts about thy shape
       Of deities or mortals, or of both,
               In Tempe or the dales of Arcady?
       What men or gods are these? What maidens loth?
What mad pursuit? What struggle to escape?
               What pipes and timbrels? What wild ecstasy?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;There are much more ... adventurous, shall we say? layouts for poems, but this was the main use case I wanted to meet: lines which respect the indentation of the author. But there was something else I wanted to achieve as well:&lt;/p&gt;
&lt;p&gt;One of the things I hate seeing, in printed poetry as well as electronic, is wrapping lines. My reasoning is this: if the author so carefully indented their poem, and broke lines in certain specific places, then that is as important to the work as the left indentation is.&lt;/p&gt;
&lt;p&gt;I really hate it when because of the limitations in width or type size, the authors&apos; poems are butchered in this way. It&apos;s not as bad as justifying everything centrally (that is a crime I will never bring myself to forgive), but at least on the web we do have the opportunity to scroll left and right if the need arises. Looking at my 2 requirements I realised that there is another common written format that satisfies both of these needs.&lt;/p&gt;
&lt;h2&gt;A sudden moment of clarity&lt;/h2&gt;
&lt;p&gt;So to recap my requirements were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1. No wrapping lines unless the author intended it&lt;/li&gt;
&lt;li&gt;2. Indentation as the author intended&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I realised that the way we represent code is quite similar. Along with indented lines that don&apos;t wrap, we also have syntax hilighting, so it&apos;s easier to see what the code was doing. But I realised that was simply an implementation detail, and I could use some things that I already had in order to render poetry correctly on my blog.&lt;/p&gt;
&lt;h2&gt;The Backend&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https://www.webiny.com&quot;&gt;Webiny, the Serverless CMS&lt;/a&gt;, for the backend. I&apos;d already configured an &lt;a href=&quot;https://editorjs.io/&quot;&gt;Editor.js&lt;/a&gt; extension &lt;a href=&quot;https://github.com/editor-js/code&quot;&gt;to render code blocks&lt;/a&gt;. I forked this code and added to my Webiny admin app&apos;s HeadlessCMS config (&lt;a href=&quot;https://github.com/endymion1818/backends-webiny/pull/3/files&quot;&gt;full PR here&lt;/a&gt;), and after a few minor modifications I had the ability to add a block of poetry:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;So far so good. I don&apos;t mind that the display looks like a code block, for me it&apos;s a minor consideration and actually the monospace helps me to see where the indentation occurs.&lt;/p&gt;
&lt;h2&gt;Rendering on the Frontend&lt;/h2&gt;
&lt;p&gt;Now that I have my code block coming through with an appropriate field type identifier, I could modify my frontend Astro app to render blocks of poetry as part of the rich text renderer, which I&apos;d already built.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      {field.type === &apos;poetry&apos; &amp;amp;&amp;amp; (
        &amp;lt;RenderPoetry poetry={field.data.poetry} /&amp;gt;
      )}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I admit this solution was &lt;a href=&quot;https://github.com/endymion1818/personal-frontends-monorepo/pull/31/files&quot;&gt;much hackier than it needed to be&lt;/a&gt;, but it works for now. I hope I will have time to create a proper theme for Shiki so I don&apos;t have to forcibly override the inline CSS with some !important declarations.&lt;/p&gt;
&lt;p&gt;You can see both poetry blocks and code rendered in this article. And I hope it&apos;ll mean I&apos;m finally more free to add more articles about poetry here. Time, and children, willing of course!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Publishing Poetry with Webiny CMS and Astro&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A recipe for quick and dirty ramen</title><link>https://deliciousreverie.co.uk/posts/quick-and-dirty-ramen/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/quick-and-dirty-ramen/</guid><description>I&apos;ve been slowly perfecting a recipe for ramen that I can quickly make at home for a quick meal. If you too like that sort of thing ... here&apos;s my hopefully simple recipe! </description><pubDate>Tue, 28 Jan 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I really like asian food. I&apos;ll go for gyoza / jiaozi any day over a &quot;traditional&quot; sunday roast. One thing I love most is ramen, but it&apos;s taken me ages to invent my own recipe.&lt;/p&gt;
&lt;p&gt;So this is a very quick and loose recipe, please don&apos;t stick to it, I&apos;m sure it can be improved upon!&lt;/p&gt;
&lt;h2&gt;Ingredients &lt;a href=&quot;https://deliciousreverie.co.uk/posts/quick-and-dirty-ramen/#ingredients&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1 pack miso soup (one of the paste varieties are best)&lt;/li&gt;
&lt;li&gt;Soy sauce (light or dark, I can&apos;t decide)&lt;/li&gt;
&lt;li&gt;Small amount of sesame oil&lt;/li&gt;
&lt;li&gt;1 spring onion, finely chopped&lt;/li&gt;
&lt;li&gt;I egg, fried or boiled&lt;/li&gt;
&lt;li&gt;A little meat (optional), pork or chicken&lt;/li&gt;
&lt;li&gt;Small quantity of chopped pepper&lt;/li&gt;
&lt;li&gt;Pinch of garlic (to taste)&lt;/li&gt;
&lt;li&gt;Small quantity of ginger (to taste)&lt;/li&gt;
&lt;li&gt;dried chilli&lt;/li&gt;
&lt;li&gt;Rice noodles&lt;/li&gt;
&lt;li&gt;Boiled water&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Method &lt;a href=&quot;https://deliciousreverie.co.uk/posts/quick-and-dirty-ramen/#method&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Put the noodle into a bowl. Pour in the boiled water. Whilst they&apos;re getting ready, fry your egg. I like to keep it a little runny, so it mixes into the soup a little.&lt;/p&gt;
&lt;p&gt;You could fry the other ingredients (except the spring onion) together beforehand, but that&apos;s just a little nicer if you have time. Otherwise, throw them into the hot water.&lt;/p&gt;
&lt;p&gt;Once all the ingredients are in, add the spring onion.&lt;/p&gt;
&lt;p&gt;If the water has cooled down or the noodles haven&apos;t absorbed water enough, put the bowl in a microwave for a minute or so.&lt;/p&gt;
&lt;p&gt;Add the egg, and serve with chopsticks.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Variations &lt;a href=&quot;https://deliciousreverie.co.uk/posts/quick-and-dirty-ramen/#variations&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve sometimes used mackerel fried in Lao Gan Ma&apos;s crunchy chilli paste, instead of the meat. That stuff is awesome and goes with almost anything.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A recipe for quick and dirty ramen&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>React Accessibility</title><link>https://deliciousreverie.co.uk/posts/react-accessibility/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/react-accessibility/</guid><description>In the recent past, I&apos;ve heard React being criticised for not being accessible, or for allowing practices that don&apos;t help developers think about accessibility. This is an article I wrote for the print magazine Net. </description><pubDate>Mon, 24 Dec 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Originally published in print for &lt;a href=&quot;https://www.creativebloq.com&quot;&gt;Net Magazine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the recent past, I&apos;ve heard React being criticised for not being accessible, or for allowing practices that don&apos;t help developers think about accessibility.&lt;/p&gt;
&lt;p&gt;I have to disagree.&lt;/p&gt;
&lt;p&gt;Just like any other frontend code, working with React in an accessible way requires some forethought.&lt;/p&gt;
&lt;p&gt;One of the common mistakes I&apos;ve seen is that we sometimes put click events on non-semantic elements. It&apos;s true, React makes it very easy to put a click event on any dom element.&lt;/p&gt;
&lt;p&gt;I think one of the reasons for this is that with React, our JavaScript is our single source of truth, so we tend to think in JavaScript. One DOM element is like any other DOM element, and they all can be manipulated in much the same way.&lt;/p&gt;
&lt;p&gt;However, if we&apos;re thinking about accessibility, we&apos;re committing ourselves before we start coding, that we&apos;re going to do things the right way, including using semantic elements. If we do this, we&apos;ll ensure we assign a click event to a semantic element to start with - a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; or an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag - or we&apos;ll do all the extra legwork that&apos;s required to make up for it.&lt;/p&gt;
&lt;h2&gt;Encourage Accessibility in Components &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-accessibility/#encourage-accessibility-in-components&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another way to encourage others using your components to think in an accessible way is to compose components to include accessibility features. For example, this component passes down a prop that&apos;s an image source.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src={this.prop.imgurl} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What are we missing here? The &lt;code&gt;alt&lt;/code&gt; attribute. How do we encourage accessibility in this instance? Wrap the component in a conditional, so that it doesn&apos;t render without the &lt;code&gt;alt&lt;/code&gt; tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{this.props.imgalt ? (    
    &amp;lt;img src={this.prop.imgurl} alt={this.props.imgalt} /&amp;gt;  
) : null}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could go a bit further with this, and check that the length of imgaltis greater than 0.&lt;/p&gt;
&lt;h2&gt;It Starts &amp;amp; Ends With Accessibility &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-accessibility/#it-starts-and-ends-with-accessibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Just as our thinking process starts with the commitment to code with accessibility in mind, our launched product should finish with accessibility. So test your assumptions:- did you really build an accessible site or app?&lt;/p&gt;
&lt;p&gt;Use software to test what you can. But better than that: connect with people you know. Take your product to your friends and relatives or your audience. How do they get on using it?&lt;/p&gt;
&lt;p&gt;React has plenty of built-in support for building things in an accessible way. Check out the documentation at &lt;a href=&quot;https://reactjs.org/docs/accessibility.html&quot;&gt;https://reactjs.org/docs/accessibility.html&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The question is, will you make that commitment to yourself, and the people who will use your site?&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:React Accessibility&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Build a countdown timer using Temporal with React</title><link>https://deliciousreverie.co.uk/posts/react-countdown-timer/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/react-countdown-timer/</guid><description>As the Temporal API is now stable, and a specification compatible polyfill is available, I thought I would use Temporal to build a countdown timer in a React project.</description><pubDate>Sun, 04 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;As the &lt;a href=&quot;https://github.com/tc39/proposal-temporal&quot;&gt;Temporal API is now stable at Stage 3&lt;/a&gt;, and a specification compatible polyfill is available, I thought I would use Temporal to build a countdown timer in a React project.&lt;/p&gt;
&lt;p&gt;I had previously build this countdown timer without Temporal, but since the API I was using exposed &lt;a href=&quot;https://en.wikipedia.org/wiki/ISO_8601&quot;&gt;durations in ISO 8601 compatible strings&lt;/a&gt; it meant I was already &lt;a href=&quot;https://www.npmjs.com/package/tinyduration&quot;&gt;pulling in another library&lt;/a&gt; to handle those. So I thought I might as well utilise the &lt;a href=&quot;https://www.npmjs.com/package/@js-temporal/polyfill&quot;&gt;Temporal polyfill&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;The API is pretty straightforward as you might imagine&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import countdown from &apos;countdown&apos;
import { parse } from &apos;tinyduration&apos;

const CountdownTimer = ({ startedAt: Date, duration: string }) =&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can probably guess where we&apos;re going with this, we&apos;ll use a &lt;code&gt;useEffect()&lt;/code&gt; hook to run a &lt;code&gt;setInterval()&lt;/code&gt; which refreshes the calculated &lt;code&gt;remainingTime&lt;/code&gt; and sets it in &lt;code&gt;useState()&lt;/code&gt;. We need to memoise the calculated ending time to persist across rerenders too:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [timespan, setTimespan] = useState&amp;lt;Timespan | null&amp;gt;(null)

const parsedDuration = parse(duration)

const {
  years = 0,
  months = 0,
  weeks = 0,
  days = 0,
  hours = 0,
  minutes = 0,
  seconds = 0,
} = parsedDuration

const durationMs =
  years * 365 * 24 * 60 * 60 * 1000 +
  months * 30 * 24 * 60 * 60 * 1000 +
  weeks * 7 * 24 * 60 * 60 * 1000 +
  days * 24 * 60 * 60 * 1000 +
  hours * 60 * 60 * 1000 +
  minutes * 60 * 1000 +
  seconds * 1000

const endTime = useMemo(
  () =&amp;gt; new Date(startedAt.getTime() + durationMs),
  [startedAt.getTime(), durationMs],
)

useEffect(() =&amp;gt; {
  const updateCountdown = () =&amp;gt; {
    const now = new Date()
    const result = countdown(now, endTime) as Timespan
    setTimespan(result)
  }

  updateCountdown()
  const interval = setInterval(updateCountdown, 1000)

  return () =&amp;gt; clearInterval(interval)
}, [endTime])

const countdownHours = String(timespan?.hours ?? 0).padStart(2, &apos;0&apos;)
const countdownMinutes = String(timespan?.minutes ?? 0).padStart(2, &apos;0&apos;)
const countdownSeconds = String(timespan?.seconds ?? 0).padStart(2, &apos;0&apos;)

return (
  &amp;lt;time 
    dateTime={`PT${countdownHours}H${countdownMinutes}M${countdownSeconds}S`} 
  &amp;gt;
    {countdownHours}:{countdownMinutes}:{countdownSeconds}
  &amp;lt;/time&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this seems a little verbose, it is. &lt;code&gt;tinyduration&lt;/code&gt; only returns units of time if the parsed data has any, meaning you have to calculate around it. Also the library is not typed, meaning I had to implement my own.&lt;/p&gt;
&lt;p&gt;If you&apos;re reading this you&apos;re probably familiar with the &lt;code&gt;countdown&lt;/code&gt; library. In all honesty it doesn&apos;t do much at all apart from provide a duration until the end time you provide it.&lt;/p&gt;
&lt;p&gt;This can all be achieved much more succinctly in the Temporal API.&lt;/p&gt;
&lt;h2&gt;Switching to Temporal&lt;/h2&gt;
&lt;p&gt;The first change is to the types. Instead of a &lt;code&gt;string&lt;/code&gt; or my own implementation of the result of the &lt;code&gt;tinyduration&lt;/code&gt; library, I can use a type from &lt;code&gt;Temporal&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const [remainingTime, setRemainingTime] = useState&amp;lt;Temporal.Duration | null&amp;gt;(
  null,
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;endTime&lt;/code&gt; memoised calculation is fairly similar, only using Temporal is a bit more clear about what&apos;s happening:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const endTime = useMemo(() =&amp;gt; {
  const startInstant = Temporal.Instant.fromEpochMilliseconds(
    startedAt.getTime(),
  )
  const durationObj = Temporal.Duration.from(duration)
  const totalMs = durationObj.total({ unit: &apos;millisecond&apos; })
  return startInstant.add({ milliseconds: totalMs })
}, [startedAt, duration])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first change here is to convert the start time to a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant&quot;&gt;Temporal Instant object&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Temporal.Duration&lt;/code&gt; natively supports the ISO 8601 format, therefore calculating the total is a matter of converting the &lt;code&gt;duration&lt;/code&gt; to milliseconds, and then using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Duration/add&quot;&gt;&lt;code&gt;.add()&lt;/code&gt;&lt;/a&gt; to calculate the sum.&lt;/p&gt;
&lt;p&gt;So although &lt;code&gt;endTime&lt;/code&gt; itself slightly more verbose, we didn&apos;t have all the setup and its easier to see what&apos;s going on. I much prefer my code that way since it makes it much easier for other developers to pick up later on.&lt;/p&gt;
&lt;p&gt;Now for the &lt;code&gt;useEffect()&lt;/code&gt; hook:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  const updateCountdown = () =&amp;gt; {
    const now = Temporal.Now.instant()

    const remaining = now.until(endTime, {
      largestUnit: &apos;hour&apos;,
      smallestUnit: &apos;second&apos;,
      roundingMode: &apos;floor&apos;,
    })

    setRemainingTime(remaining)
  }

  updateCountdown()
  const interval = setInterval(updateCountdown, 1000)

  return () =&amp;gt; clearInterval(interval)
}, [endTime])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is much clearer as well: we instantiate a new Temporal &lt;code&gt;Instant&lt;/code&gt; then use the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/Instant/until&quot;&gt;&lt;code&gt;.until()&lt;/code&gt; utility&lt;/a&gt; to represent the duration from the current instant &lt;em&gt;until&lt;/em&gt; the end time.&lt;/p&gt;
&lt;p&gt;The second argument allows us to format the remaining duration in place.&lt;/p&gt;
&lt;p&gt;The final step is formatting and the render function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const countdownHours = Math.max(0, remainingTime?.hours ?? 0)
const countdownMinutes = Math.max(0, remainingTime?.minutes ?? 0)
const countdownSeconds = Math.max(0, remainingTime?.seconds ?? 0)

return (
  &amp;lt;time
    dateTime={`PT${countdownHours}H${countdownMinutes}M${countdownSeconds}S`}
  &amp;gt;
    {String(countdownHours).padStart(2, &apos;0&apos;)}:
    {String(countdownMinutes).padStart(2, &apos;0&apos;)}:
    {String(countdownSeconds).padStart(2, &apos;0&apos;)}
  &amp;lt;/time&amp;gt;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Temporal doesn&apos;t provide formatting operations suitable for this situation so we need &lt;code&gt;padStart()&lt;/code&gt; and then &lt;code&gt;Math.max()&lt;/code&gt; to ensure we don&apos;t go into negative values.&lt;/p&gt;
&lt;p&gt;I&apos;m using the raw data to set the &lt;code&gt;datetime&lt;/code&gt; attribute as ISO8601 formatted string, then formatting it again to render the content of the element.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;The function is now much shorter (56 to 83 lines) but that isn&apos;t the biggest win here.I&apos;m only using one library, and that&apos;s a polyfill which can be removed eventually.&lt;/p&gt;
&lt;p&gt;It&apos;s also much clearer. We&apos;re not using mathematical operations on milliseconds, instead treating times as their own separate entities. And I think the code is much  more readable too.&lt;/p&gt;
&lt;p&gt;My only concern with this component is it doesn&apos;t announce to screen reader users when the time counts down. However we wouldn&apos;t want to annoy the user more by announcing every second.&lt;/p&gt;
&lt;p&gt;An improvement could be that we add logic to announce the time left at meaningful intervals such as &quot;10 minutes left&quot;, &quot;1 minute left&quot; etc.&lt;/p&gt;
&lt;p&gt;All in all, the Temporal API is going to improve JavaScript a huge amount. I&apos;m really looking forward to seeing how the landscape changes over the next few years.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Build a countdown timer using Temporal with React&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>React: data fetching results in type error: object is not a function</title><link>https://deliciousreverie.co.uk/posts/react-data-fetching-typerror-object-is-not-a-function/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/react-data-fetching-typerror-object-is-not-a-function/</guid><description>Sometimes I get frustrated with React, there can still sometimes be obscure errors that are a result of it&apos;s component lifecycle methods. But at least it means I can tell you about how to avoid them ...</description><pubDate>Mon, 12 Jul 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Sometimes I get frustrated with React, there can still sometimes be obscure errors that are a result of it&apos;s component lifecycle methods. But at least it means I can tell you about how to avoid them ...&lt;/p&gt;
&lt;p&gt;Take a look at this code a second. There&apos;s nothing wrong with the fetch() resolution, the API works fine. There&apos;s nothing wrong with the render method either.&lt;/p&gt;
&lt;p&gt;But if you try to run this code you&apos;ll get some error like &lt;code&gt;Error: Objects are not valid as a React child (found: [object Promise])&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function getFacts() {
  const response = await fetch(&quot;https://cat-fact.herokuapp.com/facts/random?amount=10&quot;)
  if (!response.ok) {
    throw new Error(‘error’)
  }
  const facts = await response.json()
  return facts
}
export default async function Home() {
  const facts = await getFacts();
  return (
    &amp;lt;div&amp;gt;
    {facts &amp;amp;&amp;amp; facts.map(fact =&amp;gt; (
      &amp;lt;div key={fact.id}&amp;gt;test&amp;lt;/div&amp;gt;
    ))}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is that the data you fetched isn&apos;t consumed by the components&apos; lifecycle. Yet there&apos;s nothing to tell you that in the error. Here&apos;s what you need to do to resolve it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useEffect, useState } from &apos;react&apos;

async function getFacts() {
  const response = await fetch(&quot;https://cat-fact.herokuapp.com/facts/random?amount=10&quot;)
  if (!response.ok) {
    throw new Error(‘error’)
  }
  const facts = await response.json()
  return facts
}
export default async function Home() {
  const [facts, setFacts] = useState()

  useEffect(() =&amp;gt; {
      getFacts().then(facts =&amp;gt; setFacts(facts))
  }, [])

  return (
    &amp;lt;div&amp;gt;
    {facts &amp;amp;&amp;amp; facts.map(fact =&amp;gt; (
      &amp;lt;div key={fact.id}&amp;gt;test&amp;lt;/div&amp;gt;
    ))}
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You see that we imported the &lt;code&gt;useEffect&lt;/code&gt; and &lt;code&gt;useState&lt;/code&gt; hooks, then when the component mounts, we fetch the data, and pass it to state using setFacts.&lt;/p&gt;
&lt;p&gt;Now, once the data has been retrieved, you won&apos;t get that error any more.&lt;/p&gt;
&lt;p&gt;It&apos;s a simple fix for an issue that wouldn&apos;t have occurred except for the fact that we&apos;re in React, and we need to respect the React component lifecycle, and it&apos;s way of handling data. We can&apos;t fetch data and assume we can just use it.&lt;/p&gt;
&lt;p&gt;When in Rome ...&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:React: data fetching results in type error: object is not a function&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>A React performance case study</title><link>https://deliciousreverie.co.uk/posts/react-performance-case-study/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/react-performance-case-study/</guid><description>Part of the way through a project to replace an app with a static site generated by Gatsbyjs, initial tests revealed that we there was a lot we could do to improve the performance. These are the steps we took to cut load times to under 3 seconds. </description><pubDate>Wed, 12 Dec 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Part of the way through a project to replace an app with a static site generated by Gatsbyjs, initial tests revealed that we there was a lot we could do to improve the performance. Here&apos;s how we turned the project around, cutting load times to under 3 seconds and reducing percieved performance bottlenecks.&lt;/p&gt;
&lt;p&gt;Here&apos;s what the initial test looked like in web page test (&lt;a href=&quot;http://webpagetest.org/&quot;&gt;webpagetest.org&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Identifying the Problem &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#identifying-the-problem&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The initial issue we had was how do we identify what problems we had that we needed to solve. Without a clear understanding of what was going wrong, we had no way of focusing on tasks that could help us improve the current situation.&lt;/p&gt;
&lt;p&gt;One of the first things we did was to find out how our site was rendering across different browsers. Straight away, we noticed that browers that didn&apos;t have the benefit of a fast JavaScript parser were significantly slower. Sometimes it would take up to about 14 seconds to render a page. Interacting with key conversion-linked elements on the page was janky or stuttered a lot, which further put users off.&lt;/p&gt;
&lt;p&gt;We also had a popup module for cookies which took over a large part of the screen, and which couldn&apos;t be dismissed until all of the JS was downloaded and parsed. This was a major issue to overcome.&lt;/p&gt;
&lt;p&gt;Over later iterations we also implemented a few other strategies to help us pinpoint issues:&lt;/p&gt;
&lt;h3&gt;Webpagetest &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#webpagetest&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web page test has got better &amp;amp; better over the years. It provides a teriffic overview as well as a deep dive into the waterfall and has tonnes of settings for different locations and devices.&lt;/p&gt;
&lt;p&gt;It&apos;s still my favourite performance tool.&lt;/p&gt;
&lt;h3&gt;Lighthouse &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use the Lighthouse plugin for Google Chrome (instead of the native version) because it contains newer features that we wanted to incorporate into our results. Lighthouse is typically better for pinpointing different overall issues such as PWA status and accessibility.&lt;/p&gt;
&lt;h3&gt;HotJar &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#hotjar&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I was skeptical about using HotJar to begin with. I felt it would add to our JavaScript burden instead of reduce it. However, we managed to integrate HotJar with our analytics tracking script, reducing the overall load. The script was loaded asynchronously which helped. However, downloading and parsing still took up processing power, reducing the interactivity of the site until it was completed. So reducing the overall JS burden was still a necessity.&lt;/p&gt;
&lt;p&gt;HotJar later became instrumental in helping us identify a severe issue which resulted in a white screen, so it became really useful in our testing and iterating process.&lt;/p&gt;
&lt;h3&gt;Splunk &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#splunk&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Splunk allows you to collect errors in your application and log them for later use offsite. I particularly love Splunk, because it allowed us to send any JavaScript errors we wanted that occured on the client off to be examined and quantified later. Once we had implemented Splunk (using React&apos;s ErrorBoundary API) we could see what was really going on with our app in the wild.&lt;/p&gt;
&lt;p&gt;Having set up these two, we were ready to dive into prioritising and fixing some of the significant issues we were seeing.&lt;/p&gt;
&lt;h2&gt;Issue 1: Large JavaScript Burden &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-1:-large-javascript-burden&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even though GatsbyJS does a huge amount to reduce the amount of JavaScript (rendering what it can on the server, tree shaking and minification to name a few) We had 2 issues with our JavaScript:&lt;/p&gt;
&lt;h3&gt;1. Pulling Too Much onto the Frontend &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#1.-pulling-too-much-onto-the-frontend&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GatsbyJS&apos; compiler looks for code that includes references to the windowobject and renders those on the client. The rest, it renders on the server. We had a lot of extra JavaScript, including page configuration objects, that ran on the frontend instead of using the Gatsbyjs&apos; data layer.&lt;/p&gt;
&lt;p&gt;By refactoring back into gatsby, we were able to reduce our bundle size.&lt;/p&gt;
&lt;p&gt;We also noticed that somehow larger libraries like Lodash were used to render client-side content. By refactoring this library out of what was rendered on the client side, we further streamlined it.&lt;/p&gt;
&lt;h3&gt;2. Using WebPack 3 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#2.-using-webpack-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We moved the site from GatsbyJS v1 to v2 (it took a day to complete). this led to a reduction in JS bundle size from about 700kb to around 530kb.&lt;/p&gt;
&lt;h2&gt;Issue 2: JavaScript Caching &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-2:-javascript-caching&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using Webpack 3 was a huge step forward. Previously, in Gatsby v1, we could cache HTML really well. But page components couldn&apos;t be cached. Webpack 3 allowed us to cache JS more aggresively, leading to much better performance.&lt;/p&gt;
&lt;p&gt;After the work with Webpack we saw the following results:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Issue 3: Obtrusive Cookie Module &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-3:-obtrusive-cookie-module&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I mentioned above, we noticed that on mid-range phones where users didn&apos;t use a fast JavaScript parser, it could take up to 14s for the site to be interactive. Users could still get some functionality and scroll around ... however, they were prevented from doing so by the cookie module. This was a particular issue on smaller screens but affected everyone.&lt;/p&gt;
&lt;p&gt;To combat the issue we redesigned the module to be much smaller. We also set a timeout on the module so that it wouldn&apos;t try to check for cookies or even render until after the dom was loaded.&lt;/p&gt;
&lt;p&gt;As a result, we didn&apos;t stop people from using the site until the cookie module was dismissed.&lt;/p&gt;
&lt;h2&gt;Issue 4: Large Dataset &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-4:-large-dataset&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a high-converting page we made a call on the frontend to get a large dataset that was over 1.3MB. This was a stop-gap: we ultimately needed to get this dataset not from a static JSON file but from a server that could take 35ms to compile the data we needed and make it available as a JSON object.&lt;/p&gt;
&lt;p&gt;Instead of querying this API directly we built an intermediary service that cached the data set we needed as a static JSON file. The API also reduced the data down by eliminating extra data that we didn&apos;t use.&lt;/p&gt;
&lt;p&gt;This reduced our file to around 650KB.&lt;/p&gt;
&lt;h2&gt;Issue 5: Long-running API Calls &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-5:-long-running-api-calls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The data we requested on the API endpoint mentioned above was mission-critical. It had to be available to our users, otherwise conversions would not be possible. Therefore we hadn&apos;t set a timeout on the API call.&lt;/p&gt;
&lt;p&gt;We were able to find a workaround to this issue by caching the data in the project on build. That allowed us to set a timeout so that if the connection dropped suddenly, a default data set that was fetched on build earlier could be displayed.&lt;/p&gt;
&lt;h2&gt;Issue 6: Large Number of JavaScript Math Functions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-6:-large-number-of-javascript-math-functions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having an intermediary service allowed us to further reduce the JavaScript on the site.&lt;/p&gt;
&lt;p&gt;The large dataset contained raw numbers down do several decimal places. But we only needed to provide 2 decimal places to visitors. Although it reduced the adaptability of the intermediary service we had built, we decided to perform rounding there instead. This freed up much more processing power for the main thread and further reduced times.&lt;/p&gt;
&lt;h2&gt;Issue 7: Avoiding Re-rendering &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-7:-avoiding-re-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;m going to cover this more in-depth in another post, but we also managed to reduce page re-renders massively. We did this using the Context API to share data across several modules via State. Most of our functions were used in State as well. This meant that only the components consuming that data re-rendered when they were used.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A recent performance test shows we have achieved the following:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I recognise these performance results are under optimal conditions and actual, not to mention percieved, results aren&apos;t captured by these figures. But we also need some achievable goals that help us to stay motivated and focused.&lt;/p&gt;
&lt;p&gt;We haven&apos;t finished yet either ... more work will continue to be done during the lifetime of this project.&lt;/p&gt;
&lt;p&gt;This has been a great adventure, and it&apos;s been exciting to see how we&apos;ve gone to fully-loaded times of around 10s to 4, then down to 2.5, where it currently stands. I hope this post gives you some clues about how you can use new tricks and old to make your apps more performant.&lt;/p&gt;
&lt;p&gt;Part of the way through a project to replace an app with a static site generated by Gatsbyjs, initial tests revealed that we there was a lot we could do to improve the performance. Here&apos;s how we turned the project around, cutting load times to under 3 seconds and reducing percieved performance bottlenecks.&lt;/p&gt;
&lt;p&gt;Here&apos;s what the initial test looked like in web page test (&lt;a href=&quot;http://webpagetest.org/&quot;&gt;webpagetest.org&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;Doc-CompleteFully--LoadedLoad timeFirst ByteStart RenderSpeed IndexFirst InteractiveTimeRequestsBytes InTimeRequestsBytes InCostFirst view4.733s0.901s2.1002266&amp;gt; 8.179s4.733s211,536kb8.055s232.691kb$$$$&lt;/p&gt;
&lt;h2&gt;Identifying the Problem &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#identifying-the-problem&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The initial issue we had was how do we identify what problems we had that we needed to solve. Without a clear understanding of what was going wrong, we had no way of focusing on tasks that could help us improve the current situation.&lt;/p&gt;
&lt;p&gt;One of the first things we did was to find out how our site was rendering across different browsers. Straight away, we noticed that browers that didn&apos;t have the benefit of a fast JavaScript parser were significantly slower. Sometimes it would take up to about 14 seconds to render a page. Interacting with key conversion-linked elements on the page was janky or stuttered a lot, which further put users off.&lt;/p&gt;
&lt;p&gt;We also had a popup module for cookies which took over a large part of the screen, and which couldn&apos;t be dismissed until all of the JS was downloaded and parsed. This was a major issue to overcome.&lt;/p&gt;
&lt;p&gt;Over later iterations we also implemented a few other strategies to help us pinpoint issues:&lt;/p&gt;
&lt;h3&gt;Webpagetest &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#webpagetest&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Web page test has got better &amp;amp; better over the years. It provides a teriffic overview as well as a deep dive into the waterfall and has tonnes of settings for different locations and devices.&lt;/p&gt;
&lt;p&gt;It&apos;s still my favourite performance tool.&lt;/p&gt;
&lt;h3&gt;Lighthouse &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#lighthouse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I use the Lighthouse plugin for Google Chrome (instead of the native version) because it contains newer features that we wanted to incorporate into our results. Lighthouse is typically better for pinpointing different overall issues such as PWA status and accessibility.&lt;/p&gt;
&lt;h3&gt;HotJar &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#hotjar&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I was skeptical about using HotJar to begin with. I felt it would add to our JavaScript burden instead of reduce it. However, we managed to integrate HotJar with our analytics tracking script, reducing the overall load. The script was loaded asynchronously which helped. However, downloading and parsing still took up processing power, reducing the interactivity of the site until it was completed. So reducing the overall JS burden was still a necessity.&lt;/p&gt;
&lt;p&gt;HotJar later became instrumental in helping us identify a severe issue which resulted in a white screen, so it became really useful in our testing and iterating process.&lt;/p&gt;
&lt;h3&gt;Splunk &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#splunk&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Splunk allows you to collect errors in your application and log them for later use offsite. I particularly love Splunk, because it allowed us to send any JavaScript errors we wanted that occured on the client off to be examined and quantified later. Once we had implemented Splunk (using React&apos;s ErrorBoundary API) we could see what was really going on with our app in the wild.&lt;/p&gt;
&lt;p&gt;Having set up these two, we were ready to dive into prioritising and fixing some of the significant issues we were seeing.&lt;/p&gt;
&lt;h2&gt;Issue 1: Large JavaScript Burden &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-1:-large-javascript-burden&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even though GatsbyJS does a huge amount to reduce the amount of JavaScript (rendering what it can on the server, tree shaking and minification to name a few) We had 2 issues with our JavaScript:&lt;/p&gt;
&lt;h3&gt;1. Pulling Too Much onto the Frontend &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#1.-pulling-too-much-onto-the-frontend&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;GatsbyJS&apos; compiler looks for code that includes references to the windowobject and renders those on the client. The rest, it renders on the server. We had a lot of extra JavaScript, including page configuration objects, that ran on the frontend instead of using the Gatsbyjs&apos; data layer.&lt;/p&gt;
&lt;p&gt;By refactoring back into gatsby, we were able to reduce our bundle size.&lt;/p&gt;
&lt;p&gt;We also noticed that somehow larger libraries like Lodash were used to render client-side content. By refactoring this library out of what was rendered on the client side, we further streamlined it.&lt;/p&gt;
&lt;h3&gt;2. Using WebPack 3 &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#2.-using-webpack-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We moved the site from GatsbyJS v1 to v2 (it took a day to complete). this led to a reduction in JS bundle size from about 700kb to around 530kb.&lt;/p&gt;
&lt;h2&gt;Issue 2: JavaScript Caching &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-2:-javascript-caching&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using Webpack 3 was a huge step forward. Previously, in Gatsby v1, we could cache HTML really well. But page components couldn&apos;t be cached. Webpack 3 allowed us to cache JS more aggresively, leading to much better performance.&lt;/p&gt;
&lt;p&gt;After the work with Webpack we saw the following results:&lt;/p&gt;
&lt;p&gt;Doc-CompleteFully--LoadedLoad timeFirst ByteStart RenderSpeed IndexFirst InteractiveTimeRequestsBytes InTimeRequestsBytes InCostFirst view3.993s0.344s1.3002098&amp;gt; 4.663s3.993s21740kb5.609s331,069kb$$&lt;/p&gt;
&lt;h2&gt;Issue 3: Obtrusive Cookie Module &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-3:-obtrusive-cookie-module&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As I mentioned above, we noticed that on mid-range phones where users didn&apos;t use a fast JavaScript parser, it could take up to 14s for the site to be interactive. Users could still get some functionality and scroll around ... however, they were prevented from doing so by the cookie module. This was a particular issue on smaller screens but affected everyone.&lt;/p&gt;
&lt;p&gt;To combat the issue we redesigned the module to be much smaller. We also set a timeout on the module so that it wouldn&apos;t try to check for cookies or even render until after the dom was loaded.&lt;/p&gt;
&lt;p&gt;As a result, we didn&apos;t stop people from using the site until the cookie module was dismissed.&lt;/p&gt;
&lt;h2&gt;Issue 4: Large Dataset &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-4:-large-dataset&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a high-converting page we made a call on the frontend to get a large dataset that was over 1.3MB. This was a stop-gap: we ultimately needed to get this dataset not from a static JSON file but from a server that could take 35ms to compile the data we needed and make it available as a JSON object.&lt;/p&gt;
&lt;p&gt;Instead of querying this API directly we built an intermediary service that cached the data set we needed as a static JSON file. The API also reduced the data down by eliminating extra data that we didn&apos;t use.&lt;/p&gt;
&lt;p&gt;This reduced our file to around 650KB.&lt;/p&gt;
&lt;h2&gt;Issue 5: Long-running API Calls &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-5:-long-running-api-calls&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The data we requested on the API endpoint mentioned above was mission-critical. It had to be available to our users, otherwise conversions would not be possible. Therefore we hadn&apos;t set a timeout on the API call.&lt;/p&gt;
&lt;p&gt;We were able to find a workaround to this issue by caching the data in the project on build. That allowed us to set a timeout so that if the connection dropped suddenly, a default data set that was fetched on build earlier could be displayed.&lt;/p&gt;
&lt;h2&gt;Issue 6: Large Number of JavaScript Math Functions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-6:-large-number-of-javascript-math-functions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having an intermediary service allowed us to further reduce the JavaScript on the site.&lt;/p&gt;
&lt;p&gt;The large dataset contained raw numbers down do several decimal places. But we only needed to provide 2 decimal places to visitors. Although it reduced the adaptability of the intermediary service we had built, we decided to perform rounding there instead. This freed up much more processing power for the main thread and further reduced times.&lt;/p&gt;
&lt;h2&gt;Issue 7: Avoiding Re-rendering &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#issue-7:-avoiding-re-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;m going to cover this more in-depth in another post, but we also managed to reduce page re-renders massively. We did this using the Context API to share data across several modules via State. Most of our functions were used in State as well. This meant that only the components consuming that data re-rendered when they were used.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/react-performance-case-study/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A recent performance test shows we have achieved the following:&lt;/p&gt;
&lt;p&gt;Doc-CompleteFully--LoadedLoad timeFirst ByteStart RenderSpeed IndexFirst InteractiveTimeRequestsBytes InTimeRequestsBytes InCostFirst view1.787s0.223s1.2001.384s&amp;gt; 2.698s1.787s21301kb2.571s33438kbq$&lt;/p&gt;
&lt;p&gt;I recognise these performance results are under optimal conditions and actual, not to mention percieved, results aren&apos;t captured by these figures. But we also need some achievable goals that help us to stay motivated and focused.&lt;/p&gt;
&lt;p&gt;We haven&apos;t finished yet either ... more work will continue to be done during the lifetime of this project.&lt;/p&gt;
&lt;p&gt;This has been a great adventure, and it&apos;s been exciting to see how we&apos;ve gone to fully-loaded times of around 10s to 4, then down to 2.5, where it currently stands. I hope this post gives you some clues about how you can use new tricks and old to make your apps more performant.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:A React performance case study&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Reading micro expressions</title><link>https://deliciousreverie.co.uk/posts/reading-micro-expressions/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/reading-micro-expressions/</guid><description>I find it fascinating that in spite of many advances in modern psychology certain myths and downright errors still persist. Micro expressions are one of these, and learning to notice them has helped me to understand people better.</description><pubDate>Thu, 07 Aug 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I find it fascinating that in spite of many advances in modern psychology certain myths and downright errors still persist. We&apos;ve become wonderfully aware of neurodiversity and the abilities and limitations that has placed on individuals. Yet there is still much to understand about our fellow humans.&lt;/p&gt;
&lt;p&gt;Micro expressions are one of these, and learning to notice them has helped me to understand people, draw out ideas and comprehend situations more fully numerous times in recent years.&lt;/p&gt;
&lt;h2&gt;What are micro expressions?&lt;/h2&gt;
&lt;p&gt;It&apos;s funny, in researching this article I could find a number of articles that help identify the emotions you might see in micro expressions, but not what they are or what opportunities they open up.&lt;/p&gt;
&lt;p&gt;Micro expressions occur when you&apos;re talking to someone, perhaps you ask them a question or they reach an important juncture in the conversation. There might be a very short pause, a very slight change in the persons demeanour, and then it&apos;s gone.&lt;/p&gt;
&lt;p&gt;That change can be for the briefest of moments. Literally milliseconds, just enough to be noticeable. I quite often notice them when someone else I&apos;m with is doing the talking. Suddenly I&apos;ll see the person glance my way, or at the floor, or even skywards for a fraction of a second, and then resume the conversation.&lt;/p&gt;
&lt;p&gt;As I said above, a lot has been done to identify the kinds of emotion a person might be experiencing when these happen. However I&apos;ve learned a pattern of response that is broader, and which can foster openness in communication.&lt;/p&gt;
&lt;h2&gt;How to utilise the opportunity&lt;/h2&gt;
&lt;p&gt;When I notice someone displaying a micro expression my immediate thought is that in some way, I&apos;m not hearing the whole story. The person might be projecting; it could be that they&apos;re telling me something because they think I want to hear it. Or because they&apos;re not emotionally ready to tell me about some detail of it yet. Or more seriously because they&apos;re concealing something that they don&apos;t want me to know for some other reason.&lt;/p&gt;
&lt;p&gt;This is an opportunity for further discussion, and a lot could be lost by ignoring it or pretending it didn&apos;t happen.&lt;/p&gt;
&lt;p&gt;I typically try to ask a viewpoint question, to try to draw out the person and help them explore the subject a bit more. For example, if we&apos;re talking about a conversation they&apos;ve previously had, I might say to them, &quot;is that all they said?&quot; because it might be a good way of opening the door to further understanding.&lt;/p&gt;
&lt;h2&gt;What not to do&lt;/h2&gt;
&lt;p&gt;We mustn&apos;t fixate on micro expressions. Calling someone out because you&apos;ve noticed they&apos;ve displayed one is only going to shut them down, and could very quickly erode your relationship with them.&lt;/p&gt;
&lt;p&gt;Instead use them as a platform for further understanding, and your relationship with them, more deeply.&lt;/p&gt;
&lt;p&gt;If they&apos;re concealing something, it might be good to think of why. Are we a person in their life they don&apos;t want to open up to fully? How could we improve that relationship?&lt;/p&gt;
&lt;h2&gt;Concluding Thoughts&lt;/h2&gt;
&lt;p&gt;There&apos;s a lot more resource on this subject out there, so if you&apos;re interested in this sort of thing, you can go and look it up.&lt;/p&gt;
&lt;p&gt;Also, am I going to regret diluting my blog&apos;s SEO by adding another, entirely new subject to it?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;Do I care about SEO?&lt;/p&gt;
&lt;p&gt;Also no.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Reading micro expressions&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Lessons from refactoring a large dashboard application</title><link>https://deliciousreverie.co.uk/posts/refactoring-large-application/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/refactoring-large-application/</guid><description>Over the past few months I&apos;ve been refactoring a large application. I learned a few things from doing it, and thought they might help someone else.</description><pubDate>Sun, 16 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Over the past few months I&apos;ve been refactoring a large application written in vanilla JavaScript. I learned a few things from doing it, and thought they might help someone else.&lt;/p&gt;
&lt;h2&gt;Lesson 1: Don&apos;t do it all at once&lt;/h2&gt;
&lt;p&gt;One of the first things I did was to add bundling so I can type check and set up a test suite. I also moved to ECMAScript modules which would also help me split my JavaScript into smaller modules later on.&lt;/p&gt;
&lt;p&gt;Of course, once you add things like that it becomes apparent that there are a lot of changes you need to make.&lt;/p&gt;
&lt;p&gt;That first PR I made quickly led to over 300 files changed. That&apos;s going to be extremely difficult for someone to review.&lt;/p&gt;
&lt;p&gt;After this we decided to make a feature branch. Then I would incrementally branch off that and open PRs against the feature.&lt;/p&gt;
&lt;p&gt;This goes a bit against the grain, I dislike trunk-based development. However for a project this large and a rewrite this extensive, it was the only practical way it could be segmented and reviewed.&lt;/p&gt;
&lt;p&gt;Having a feature branch allowed me to reduce the change size. I&apos;m not saying it was always manageable for the code reviewers, but it was at least manageable on some level.&lt;/p&gt;
&lt;h2&gt;Lesson 2: Don&apos;t be afraid to be brutal&lt;/h2&gt;
&lt;p&gt;I had a clear philosophy in mind with this project: it should be modular.&lt;/p&gt;
&lt;p&gt;It seems the original concept for the project was built that way: it consisted of a series of components that would render charts, table data and other statistics (text percentages, counters and date information).&lt;/p&gt;
&lt;p&gt;However this philosophy seemed to have been abandoned in favour of a single, massive function that was responsible for fetching all of the data, transforming it, then passing it to components.&lt;/p&gt;
&lt;p&gt;I could see the intent of this function: the developer wanted to reduce requests that were made to the API.&lt;/p&gt;
&lt;p&gt;What was less clear was what data went where in the application. In fact, sometimes it was impossible to figure it out.&lt;/p&gt;
&lt;p&gt;All data resulting from the API requests were first transformed into other structures, then channeled through a &lt;code&gt;CustomEvent&lt;/code&gt; that bundled up the resulting data into an &lt;em&gt;array&lt;/em&gt; of Promises which was then attached to the &lt;code&gt;event.data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This made it very difficult to see on a component level what was API data, what was modified data, and even where it had come from.&lt;/p&gt;
&lt;p&gt;But the core concept, a &lt;code&gt;CustomEvent&lt;/code&gt;, which could be triggered by a user input, I thought was a good way of handling state refreshes, so I kept that.&lt;/p&gt;
&lt;p&gt;I made each of my components responsible for their own data, and implemented two things on the fetch that would reduce load on the API: caching in &lt;code&gt;LocalStorage&lt;/code&gt; and a queue that would, my means of a key, identify duplicate requests and return data from &lt;code&gt;LocalStorage&lt;/code&gt; if they existed there.&lt;/p&gt;
&lt;p&gt;This led to deleting a major chunk of the old data fetching strategy. I was so glad to see it go. But it did leave holes in my application that I had to reimplement later.&lt;/p&gt;
&lt;p&gt;It was a brutal change but reduced the applications&apos; complexity by an order of magnitude.&lt;/p&gt;
&lt;h2&gt;Lesson 3: Types are your friends&lt;/h2&gt;
&lt;p&gt;I had to build all of the types from the ground up for this application. It was a struggle to figure out what on earth was going on, and sometimes even where the types would change in the original application. There were type collisions everywhere, no consistent naming convention, and variables were being overwritten all over the place.&lt;/p&gt;
&lt;p&gt;But had I not made the effort to collect those original types it would have been much more challenging to refactor later on. That refactor implemented a different API with significant type changes, which led me to delete all of the types I had collected on this first pass. But without them I think it would have taken longer. At least I could see more easily where the differences were and knew more quickly what to change.&lt;/p&gt;
&lt;p&gt;Additionally, previous developers on this project were &lt;em&gt;very fond&lt;/em&gt; of using the global scope. They would import a JavaScript library and attach it to the &lt;code&gt;window&lt;/code&gt;. This meant doing things like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const chart = new Chart()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on your experience you might not recognise that this is in actual fact&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const chart = new window.Chart()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aha now we can type it.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;declare global {
  interface Window {
    Chart: new(ctx: ChartContext, config: ChartConfig) =&amp;gt; void
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is not fun &lt;em&gt;at all&lt;/em&gt; is doing that with JQuery. If you find yourself in that unfortunate position, &lt;code&gt;JQueryStatic&lt;/code&gt; is your friend. And don&apos;t ask about mocking jQuery for unit tests ... wow that was awful.&lt;/p&gt;
&lt;h2&gt;Lesson 4: Know when to bend the rules&lt;/h2&gt;
&lt;p&gt;In the original implementation there were 2 separate codebases, one for a dashboard for individuals, another dashboard for teams.&lt;/p&gt;
&lt;p&gt;I can see why this was done: the teams module was somewhat independent of the main dashboard and I guess the original author decided it couldn&apos;t be counted on to be present.&lt;/p&gt;
&lt;p&gt;However with bundling we could now &lt;code&gt;import&lt;/code&gt; from the main codebase and reduce the technical debt by a significant amount.&lt;/p&gt;
&lt;p&gt;Many of the components went down from a few hundred lines to an import and a function call that passed down the team data that I needed.&lt;/p&gt;
&lt;p&gt;I thought this was a tradeoff that &lt;em&gt;kind of&lt;/em&gt; broke the rules of separation of concerns, but due to the fact that we were calling the same APIs, and rendering data with a lot of shared implementations, I judged that it would reduce the work that needed to be done to maintain the modules over time.&lt;/p&gt;
&lt;h2&gt;Lesson 5: Don&apos;t rely on AI to write your tests&lt;/h2&gt;
&lt;p&gt;I&apos;m going to try not to get salty here. AI did help me get started with a test suite. I managed to get it to mock my data and functions that were called in the implementation, and often it was quite close to what I wanted.&lt;/p&gt;
&lt;p&gt;But it never implemented data structures that even remotely matched the implementation. This was bad because the unit tests are &lt;em&gt;documentation&lt;/em&gt;. For me one of the main reasons I put a test suite in there was to show other people coming across this project later how the components were being used. If the data structure used in the test isn&apos;t representative of the real set, it undermines it&apos;s usefulness.&lt;/p&gt;
&lt;p&gt;Plus it really wasn&apos;t that smart and often I had to debug really difficult issues. Sometimes it even just put code comments around stuff that was already there, as if it thought I was lying or something.&lt;/p&gt;
&lt;p&gt;Use AI by all means. But only with careful guardrails. And don&apos;t let it loose on your codebase without carefully reviewing what it&apos;s changed.&lt;/p&gt;
&lt;h2&gt;Browsers have a lot of what you need&lt;/h2&gt;
&lt;p&gt;Even though I implemented bundling I really didn&apos;t import that many packages. The main one was a polyfill for the &lt;code&gt;Temporal&lt;/code&gt; API since that&apos;s not out everywhere yet and I wanted to lean on that instead of implementing Moment or some other large external library.&lt;/p&gt;
&lt;p&gt;As this was a vanilla JS application I had to think carefully about state management. I split this between the LocalStorage API and the URL. Using the URL as a state manager is really good by the way. It also meant that if users have a problem later they can share their URL which will give us a lot of information about their dashboard.&lt;/p&gt;
&lt;p&gt;Also, utilising &lt;code&gt;CustomEvents&lt;/code&gt; was great. I didn&apos;t choose to pass any data down using these events, but I used them as triggers: when a user updated some filters, changed dates, or updated their data, I could tie into the save event, do a &lt;code&gt;dispatchEvent(new CustomEvent(&quot;refreshDashboardState&quot;))&lt;/code&gt;, and each of my components, because they were listening for it, would reload nicely:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function renderMyDashboardComponent() {}

window.addEventListener(&quot;refreshDashboardState&quot;, () =&amp;gt; renderMyDashboardComponent())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simples.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This project has been my focus for a long time and it feels somewhat like the Ship of Theseus now that it&apos;s all done.&lt;/p&gt;
&lt;p&gt;What hasn&apos;t changed ironically is the way users will experience it. Aside from a few helpful updates for developers (caching can skipped by appending a query string to the URL, which will really help our Helpdesk team), it looks the same.&lt;/p&gt;
&lt;p&gt;But it uses an entirely new backend data source. And in future, any further change requests (of which we receive many) can be implemented swiftly without me running for the hills.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Lessons from refactoring a large dashboard application&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Reset Webiny State Without Redeploying</title><link>https://deliciousreverie.co.uk/posts/reset-webiny-state-without-redeploying/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/reset-webiny-state-without-redeploying/</guid><description>This post was written for Webiny&apos;s technical documentation portal. Learn how to roll back the data in your Webiny project by restoring from DynamoDB backups and Elastic Search / OpenSearch snapshots.</description><pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;It’s not always feasible to destroy your Webiny instance once you’ve started to create entries. If you need to delete entries in the Headless CMS and effectively restore your instance to a previous state, you can follow along here.&lt;/p&gt;
&lt;p&gt;This technical documentation involved a deep dive into DynamoDB backup and restore, as well as ElasticSearch (OpenSearch) snapshots.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.webiny.com/docs/infrastructure/additional-resources/reset-state-without-redeploy&quot;&gt;View the full article&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Reset Webiny State Without Redeploying&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Reusable Components with Atomic Design</title><link>https://deliciousreverie.co.uk/posts/reusable-components-in-react/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/reusable-components-in-react/</guid><description>Structuring components in a reusable, discoverable way is a challenge across teams with diverse requirements. It can be difficult for developers unfamiliar with your library. Id like to introduce the idea of Atomic Development using React and styled-components. </description><pubDate>Wed, 09 Jan 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Structuring components in a reusable, discoverable way is a challenge across teams with diverse requirements. It can be difficult for developers unfamiliar with your library. They need to learn quickly how adopt, use and discover, features it has. How do you help them do that without over-burdening your components with too much documentation? I&apos;d like to introduce the idea of Atomic Development using React and styled-components.&lt;/p&gt;
&lt;p&gt;Atomic Design is an approach to development that Brad Frost came up with a few years ago. He&apos;s published an excellent book about it which you can buy, or read online at &lt;a href=&quot;https://atomicdesign.bradfrost.com/&quot;&gt;https://atomicdesign.bradfrost.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This post originally appeared in Net Magazine issue 314 under the title &quot;Put Atomic Design into Practice&quot;&lt;/p&gt;
&lt;h2&gt;The Atomic Design Approach &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#the-atomic-design-approach&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Brad Frost&apos;s book outlined a way to structure the design of a project based on building things up from the smallest component part.&lt;/p&gt;
&lt;p&gt;For example, this could be an input field. Input fields should never be used on their own. Instead, you would make sure for accessibility you have a label associated with it. In our analogy, the input field would be the Atom, and the atom and label together would be a Molecule.&lt;/p&gt;
&lt;p&gt;You could keep building upwards in this way so that you eventually had a fully designed page using not only Atoms and Molecules, but also Organisms (for example, a complete form) and a Template which contained your complete form, and perhaps other Molecules.&lt;/p&gt;
&lt;h2&gt;Atomic Development &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#atomic-development&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On a recent project I decided to investigate whether this approach could be uilised in development. In my React project I typically use Styled Components to write my CSS styles. I therefore structured my project like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- components/
  - Atoms
  - Molecules/
  - Organisms/
  - Templates/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Atoms &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#atoms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Atoms I designated as constants which could receive props. For example, a paragraph that had a prop for the text colour.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Molecules &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#molecules&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Molecules consist of stateless functional components, often comprising of several Atomic elements and can contain some custom Atoms that relate to that specific Molecule.&lt;/p&gt;
&lt;p&gt;In the example below, we have a &quot;popout&quot; section, which has a custom MainContainer element that&apos;s only used in this Molecule. It can accept multiple props and can wrap other components.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;Organisms &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#organisms&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As we get to organisms, we start to see larger elements, bringing in several Molecules and atoms, often with components that render on the frontend as well as server, and data that might come in from an external source.&lt;/p&gt;
&lt;h3&gt;Templates &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#templates&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Templates are layouts for pages that are programmatically created from other data sources, such as the one I made below for a side project about my favourite musician:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Atomic at Zopa &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#atomic-at-zopa&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Coincidentally, Gerard Brull, a colleague of mine at &lt;a href=&quot;http://zopa.com/&quot;&gt;Zopa.com&lt;/a&gt; who works out of our office in Barcelona had also come across this idea. We discussed it as a team and decided to try to implement it in a future iteration of our shared component library.We hope that it will help our team collaboration and assist the onboarding experience for newer developers that join us.&lt;/p&gt;
&lt;h2&gt;Modularised Application Development &lt;a href=&quot;https://deliciousreverie.co.uk/posts/reusable-components-in-react/#modularised-application-development&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At Zopa we build apps and services that start small but often scale up to millions of users. Organizing our code in a way that&apos;s easy to maintain is something we must therefore take into careful consideration before we begin development.&lt;/p&gt;
&lt;p&gt;Senior Developer Alexis Mangin has shared his experience of what could be the best approach when it comes to scalable applications which could have many component parts:&lt;/p&gt;
&lt;p&gt;&quot;Categorizing files based on what they represent is an easy way of partitioning your project and has became a popular practice with developers who use patterns such as MVC. In our experience, that’s okay when you work on small applications, but it can have a tremendous effect on the team’s velocity as the application grows. One way to avoid poor-planning headaches is to structure your codebase around the concept of modules, each with its own responsibility. Creating a module means you will group a set of related components, methods and assets together, providing a public interface to be used by other modules.&quot;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@alexmngn/why-react-developers-should-modularize-their-applications-d26d381854c1&quot;&gt;Please see this post for the full article.&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Reusable Components with Atomic Design&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Sad songs say so much</title><link>https://deliciousreverie.co.uk/posts/sad-songs-say-so-much/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/sad-songs-say-so-much/</guid><description>I enjoy times when I&apos;m gleefully happy. But I also enjoy being melancholy, which quite often comes out in my musical tastes. Here are some of my favourite melancholy songs, and why I like them.</description><pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I enjoy times when I&apos;m gleefully happy. But I also enjoy melancholy, which quite often comes out in my musical tastes. Here are some of my favourite melancholy songs, and why I like them.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&apos;m not going to include any instrumental songs here, though I could. I find instrumental songs so abstract that what I consider to be melancholic someone else could experience a totally different way. This is also mostly a study of the lyrics of my favourite sad songs.&lt;/p&gt;
&lt;p&gt;Honestly, I think people either prefer lyrics or melody in music. For me, lyrics is the main thing, though you can hardly have one without the other.&lt;/p&gt;
&lt;h2&gt;Sad songs say so much by Elton John&lt;/h2&gt;
&lt;p&gt;Had to include this, since I lifted the title straight from this song. I love the irony of the line &quot;when all hope is gone / tune in and turn &apos;em on&quot;, like that&apos;s going to help at all.&lt;/p&gt;
&lt;p&gt;But, as the line goes, misery does like company.&lt;/p&gt;
&lt;h2&gt;Sweetheart Like You by Bob Dylan&lt;/h2&gt;
&lt;p&gt;Sweetheart like you is one of my favourite down-and-out songs. I say that because the narrator (it&apos;s not Bob, it&apos;s never Bob) seems to be in a bad place. I get the idea that the setting is some greasy spoon restaurant on the wrong side of town, and that he is facing a crippling loss of some kind.&lt;/p&gt;
&lt;p&gt;The trauma of that kind of loss can sometimes make us reach out and focus on others as a kind of coping mechanism. We don&apos;t want to think about ourselves so we invest ourselves in others.&lt;/p&gt;
&lt;p&gt;The narrator is trying to give hope to someone else who perhaps is living with a challenging situation not unlike his own albeit with slightly more stability, enough that they can hold down a job at least.&lt;/p&gt;
&lt;h2&gt;Key West by Bob Dylan&lt;/h2&gt;
&lt;p&gt;I can&apos;t go without mentioning Key West here. In my mind&apos;s eye I see the narrator (Bob?) spending his final days looking out to sea from this location. He&apos;s singing about finding immortality but in reality knowing it beyond his grasp.&lt;/p&gt;
&lt;p&gt;Bob is getting on a bit now, and that makes this song even more poignant.&lt;/p&gt;
&lt;h2&gt;Aberdaron by Bwncath&lt;/h2&gt;
&lt;p&gt;The Welsh language music scene is incredible. I have purchased so many tracks and full albums from Welsh artists in the last year, not just as an exercise for language learning, but just because I enjoy them so much. Aberdaron is one I find myself thinking about again and again.&lt;/p&gt;
&lt;p&gt;The lead singer&apos;s striking vocals really lean into this song, accompanied by the thought provoking lyrics which focus on a time in the narrator&apos;s future where they can rest in a homely cottage by the sea, take their ease &quot;beyond all praise and judgement&quot; and gaze out to the rocks of Aberdaron.&lt;/p&gt;
&lt;p&gt;The real stinger in the story is that, if you know the town in question, the best view of the rocks is from the graveyard just up from the beach.&lt;/p&gt;
&lt;h2&gt;Fin&lt;/h2&gt;
&lt;p&gt;That&apos;s it I guess. Thought I had a few more to talk about, but it seems I don&apos;t.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Sad songs say so much&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The search service landscape</title><link>https://deliciousreverie.co.uk/posts/search-service-landscape/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/search-service-landscape/</guid><description>Search is a massive growth market but there is only one market leader and many inadequate contenders. Here&apos;s why I think we should be building more search providers to expand the market. </description><pubDate>Fri, 28 Aug 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Search is a massive growth market but there is only one market leader and many inadequate contenders. Here&apos;s why I think we should be building more search providers to expand the market.&lt;/p&gt;
&lt;p&gt;When did you last use a site search tool? What engine powered that tool? I can pretty much guess what most of you would reply. And whilst they are by far the market leaders, I think that&apos;s an indication that search as a market is about to explode.&lt;/p&gt;
&lt;h2&gt;History of search &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#history-of-search&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Building a basic search implementation was often something that was baked into monolithic apps. For example, WordPress comes with a no-frills search built in.&lt;/p&gt;
&lt;p&gt;However we&apos;ve gotten past monolithic do-everything-in-one-codebase apps now. Search becomes it&apos;s own concern, which is better for the search tool, and for the person using it, because we have the option to swap tools that aren&apos;t doing their job well enough.&lt;/p&gt;
&lt;p&gt;Whilst Apache Solr and Elastic Search are the granddaddies of search, they are in themselves extremely powerful, out-of-the box platforms. I am going to exclude them from the criteria because they&apos;re more used for logging, processing and analysing hefty amounts of data such as event logs, application errors and things like that.&lt;/p&gt;
&lt;p&gt;The use cases I&apos;m specifically thinking about are site search, app search and searches for online documentation.&lt;/p&gt;
&lt;h2&gt;The contenders &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#the-contenders&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Search is an emerging market and not nearly as well developed as the CMS market. But there are a few up and coming options.&lt;/p&gt;
&lt;h3&gt;1. Algolia &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#1.-algolia&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Algolia is the shining beacon here. It is clear from their implementation that they saw a gap in the market and have exploited it masterfully. They&apos;ve also got the most unbelievable marketing department, so good that you might be pursuaded to believe there are no contenders for the throne (which is almost true ...)&lt;/p&gt;
&lt;p&gt;Pros: Lovely analytics dashboard, delightful SDKCons: Closed source platform that can get really expensive ... just mention &quot;single sign-on&quot; and watch the dollar signs roll...Link: &lt;a href=&quot;https://www.algolia.com/&quot;&gt;https://www.algolia.com&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;2. Cludo &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#2.-cludo&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cludo are an up and coming SaaS platform not unlike Algolia, with a nice dashboard and machine learning backed features. I really want to like Cludo ... but they currently only offer a web scraper as a tool. Having seen the interface, I can say it&apos;s a really good scraper with a good set of features, but it&apos;s still a content scraper. In the use case I had, I needed to upload indexes at the time we built them, and we couldn&apos;t rely on that kind of tool. If they extended their product to include API uploads, it would be a much stronger product in my opinion.&lt;/p&gt;
&lt;p&gt;UPDATE: Cludo team have told me they do have a data push API, so you don&apos;t need to use their site crawler. More info: &lt;a href=&quot;https://docs.cludo.com/#data-indexing_push&quot;&gt;https://docs.cludo.com/#data-indexing_push&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pros: Good contender for the marketCons: No SDK.Link: &lt;a href=&quot;https://www.cludo.com/&quot;&gt;https://www.cludo.com&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;3. SwiftType &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#3.-swifttype&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;SwiftType have recently been bought out by Elastic, and is now called &quot;Elastic Site Search&quot;. Again, a good tool with two strong products: app search and site search. But again this product falls into the trap of only allowing content in via web scraper.&lt;/p&gt;
&lt;p&gt;Pros: Backing of Elastic, a leader in the landscapeCons: No content uploads API. No SDK.Link: &lt;a href=&quot;https://swiftype.com/&quot;&gt;https://swiftype.com&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;4. Meili &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#4.-meili&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Meili is an outlier in this space, and an entirely new product that isn&apos;t built on any existing technologies (most of the other platforms rely on Elastic under the hood). Meili is an open-source, self-hosted solution (I hope they&apos;ll offer it as a Saas in the future too), written in Rust. It is in active development, and possibly pre-alpha as I write this. There&apos;s no dashboard which seems a shame, but there is a good set of SDKs and &lt;a href=&quot;https://github.com/meilisearch/integration-guides&quot;&gt;integration guides on their GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pros: Self hosted, fast as heck. Upload content via APICons: No analytics dashboardLink: &lt;a href=&quot;https://www.meilisearch.com/&quot;&gt;https://www.meilisearch.com&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;5. TypeSense &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#5.-typesense&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;m adding Typesense about a month after I published this original article, since I&apos;ve only just found it.&lt;/p&gt;
&lt;p&gt;Typesense is, like Meili, open source, with a &quot;cloud&quot; version that you can sign up to as well. Typesense seems to have its positioning right, as a search API that you can install from a Docker image or on a Linux server via apt or yum package managers. There&apos;s also API libraries in JavaScript, PHP, Python and Ruby. The JS package in particular seems to come at a reasonable bundle size.&lt;/p&gt;
&lt;p&gt;The interesting thing about this is it&apos;s written in C++, so it&apos;s likely to be pretty fast too.&lt;/p&gt;
&lt;p&gt;Shoutout to Christopher Geary for letting me know about this one.&lt;/p&gt;
&lt;p&gt;Pros: Self hosted and cloud optionCons: No analytics dashboardLink: &lt;a href=&quot;https://typesense.org/&quot;&gt;https://typesense.org&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/search-service-landscape/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If I was an indie hacker or an investor with some developer punch, I&apos;d be building an Algolia clone right now. Whilst they are clearly market leaders there&apos;s plenty of room for growth here, only one or two key features hold back the expansion into a mature market with a good amount of choice.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The search service landscape&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Selecting parents of a nested element with the css :has selector</title><link>https://deliciousreverie.co.uk/posts/selecting-parents-of-nested-element-css-has-selector/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/selecting-parents-of-nested-element-css-has-selector/</guid><description>I was playing around with the :has selector recently and noticed it&apos;s got a pretty awesome feature: relative selectors.</description><pubDate>Wed, 20 Dec 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I was playing around with the :has selector recently and noticed it&apos;s got a pretty awesome feature: relative selectors. They&apos;re &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selector_structure#relative_selector&quot;&gt;defined&lt;/a&gt; as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;a selector representing an element relative to one or more anchor elements preceded by a combinator. Relative selectors that don&apos;t begin with an explicit combinator have an implied descendant combinator.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That means that &lt;code&gt;:has()&lt;/code&gt; is really smart, anything in the brackets behaves like it does in normal CSS.&lt;/p&gt;
&lt;p&gt;This really helped me because my DOM structure looks something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;element&quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;div id=&quot;element-i-want-to-target&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;According to &lt;a href=&quot;https://stackoverflow.com/questions/75823859/how-to-use-css-has-selector-for-nested-elements#answer-75824035&quot;&gt;this stackoverflow answer&lt;/a&gt;, I would need to write something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.element:has(div &amp;gt; div &amp;gt; div#element-i-want-to-target) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(This is Drupal by the way, which seems to love nested &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s even more than React)&lt;/p&gt;
&lt;p&gt;The trouble with that is that it didn&apos;t work as I had expected. It never targeted the element correctly, and after playing around I couldn&apos;t get it to respond correctly.&lt;/p&gt;
&lt;p&gt;By chance experimentation I came up with this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.element:has(#element-i-want-to-target) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And it worked! Even though the targeted element was 3 &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s deep, it&apos;s relative to the parent &lt;code&gt;.element&lt;/code&gt;, and so &lt;code&gt;:has()&lt;/code&gt; evaluated it as I desired.&lt;/p&gt;
&lt;p&gt;This makes sense &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selector_structure#relative_selector&quot;&gt;given the MD docs&lt;/a&gt;, but it&apos;s still not all that clear, I wish that MDN would have shown the HTML structure along with its examples.&lt;/p&gt;
&lt;p&gt;But it makes sense if you think about it: by using &lt;code&gt;:has()&lt;/code&gt; you&apos;re scoping your CSS to the parent you&apos;re using it on; anything appearing &lt;em&gt;inside&lt;/em&gt; that pseudoselector works just like normal CSS does.&lt;/p&gt;
&lt;p&gt;I do love these recent additions to CSS, it&apos;s made it so awesome to work with.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Selecting parents of a nested element with the css :has selector&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Sending Emails via SendGrid with Cloudflare Functions</title><link>https://deliciousreverie.co.uk/posts/sending-emails-with-cloudflare-functions/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/sending-emails-with-cloudflare-functions/</guid><description>SendGrid remains a popular tool for routing emails in today&apos;s applications. But it isn&apos;t without pitfalls. Here&apos;s my setup.</description><pubDate>Thu, 18 Jan 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;SendGrid remains a popular tool for routing emails in today&apos;s applications. But it isn&apos;t without pitfalls. Here&apos;s my setup.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I am very grateful &lt;a href=&quot;https://blog.amanbhargava.com/send-email-using-cloudflare-worker/&quot;&gt;to Aman for his article&lt;/a&gt;, which this article updates to reflect changes in the Cloudflare Functions API, some issues I found with SendGrid, and some other useful stuff like local development with Wrangler.&lt;/p&gt;
&lt;p&gt;First of all, don&apos;t try to use SendGrid&apos;s own library with CloudFlare functions. It has a lot of cross-dependencies with Node.js, and CF functions are decidedly &lt;em&gt;not&lt;/em&gt; Node.JS. At some point it&apos;s going to try to call &lt;code&gt;crypto&lt;/code&gt; and &lt;code&gt;fs&lt;/code&gt;, neither of which exist in the V8 engine.&lt;/p&gt;
&lt;h1&gt;Part 1: Semantics&lt;/h1&gt;
&lt;p&gt;The first thing to confuse me was the terminology Cloudflare uses for its products. It took me a while to realise that Workers were separate from Functions. I think the terminology is sometimes interchangeable in their marketing which doesn&apos;t help.&lt;/p&gt;
&lt;p&gt;The APIs relating to &lt;em&gt;Functions&lt;/em&gt; (not workers) are &lt;a href=&quot;https://developers.cloudflare.com/pages/functions/&quot;&gt;on their docs site&lt;/a&gt;. The Workers API is different (although I am still confused about the delineation of these products because they both seem to use the V8 engine).&lt;/p&gt;
&lt;p&gt;The setup for a new worker is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function onRequest(context) {
  const { request, env } = context;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Definitely don&apos;t use &lt;code&gt;export default&lt;/code&gt; here, it works fine locally with Wrangler but when you deploy it your logs will say that it couldn&apos;t find any valid function entry point.&lt;/p&gt;
&lt;p&gt;I passed the &lt;code&gt;request&lt;/code&gt; object around a fair bit in other functions, but do be careful about &lt;code&gt;clone&lt;/code&gt;ing the object. At the time of writing, Cloudflare functions have a very minimal memory allocation and cloning objects leads to a warning message, which can lead to functions crashing.&lt;/p&gt;
&lt;p&gt;Be careful about how you utilise &lt;code&gt;request&lt;/code&gt; in your functions. If I could be more specific about that particular iceberg, I would. It led to quite a long period of debugging for me.&lt;/p&gt;
&lt;p&gt;This seems to work OK though. As Aman did in his article, I&apos;m first catching all other requests that are not &lt;code&gt;POST&lt;/code&gt;s and rendering a &quot;not found&quot; message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function onRequest(context) {
  const { request, env } = context;

  if (request.method !== &quot;POST&quot;) {
    return await handleDisallowedMethod(request, env);
  }
  return await handlePostRequest(request, env);
}

async function handleDisallowedMethod() {
  return new Response(&quot;Object Not Found&quot;, {
    statusText: &quot;Object Not Found&quot;,
    status: 404,
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Environment Variables&lt;/h2&gt;
&lt;p&gt;You might notice that I&apos;m also anticipating that there are some &lt;code&gt;env&lt;/code&gt; variables being sent with this request. This wasn&apos;t trivial to set up in Wrangler. It&apos;s totally non standard. And because Wrangler is built for Cloudflare Workers as well as Cloudflare Functions, there are 2 ways of supplying variables. I got confused by this and tried to do it the wrong way. Cue 4 more hours of painful debugging. Here&apos;s the code that should be in your &lt;code&gt;.dev.vars&lt;/code&gt; file locally, which will get picked up in the build step:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SENDGRID_API_KEY=
SENDGRID_EMAIL_RECIPIENT=me@example.com
SENDGRID_EMAIL_SENDER=mywebsite@example.com

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure that&apos;s excluded from your Git history of course. And be kind to your teammates and make sure you include a &lt;code&gt;.dev.vars.example&lt;/code&gt;, which is checked in to Git.&lt;/p&gt;
&lt;h2&gt;Handling the Request&lt;/h2&gt;
&lt;p&gt;Now let&apos;s handle our request. First we need to get the referrer URL so we can redirect the user back to the website.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function handlePostRequest(request, env) {
  const returnUrl = request.headers.get(&quot;referer&quot;);

  let formData = await readRequestBody(request);
  const requestBody = composeRequest(formData, env);

  // ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;ll pause there because I need to tell you what&apos;s happening in &lt;code&gt;readRequestBody&lt;/code&gt; and &lt;code&gt;composeRequest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;readRequestBody&lt;/code&gt; is mostly unchanged from Aman&apos;s implementation, except I didn&apos;t think it was necessary to go too deep; I am only anticipating requests that contain JSON and formData.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function readRequestBody(request) {
  const { headers } = request;
  const contentType = headers.get(&quot;content-type&quot;);
  if (contentType.includes(&quot;application/json&quot;)) {
    const body = await request.json();
    return body;
  } else if (contentType.includes(&quot;form&quot;)) {
    const formData = await request.formData();
    let body = {};
    for (let entry of formData.entries()) {
      body[entry[0]] = entry[1];
    }
    return body;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Composing the API Request Body&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;composeRequest&lt;/code&gt; is a little more structured than originally. I wanted to compose this in place rather than passing things around too much, so I ended up with this implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function composeRequest(formData, env) {
  const { email, firstname, surname, textarea } = formData;
  return {
    from: {
      email: env.SENDGRID_EMAIL_SENDER,
      name: &quot;my website&quot;,
    },
    replyTo: {
      email: `${email}`,
      name: `${firstname} ${surname}`,
    },
    subject: &quot;New message from my website&quot;,
    content: [
      {
        type: &quot;text/plain&quot;,
        value: `New message from ${firstname} ${surname} (${email}): &quot;${textarea}&quot;`,
      },
    ],
    personalizations: [
      {
        from: {
          email: env.SENDGRID_EMAIL_SENDER,
          name: &quot;my website (example.com)&quot;,
        },
        to: [
          {
            email: env.SENDGRID_EMAIL_RECIPIENT,
            name: &quot;Recipient&quot;,
          },
        ],
      },
    ],
  };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here&apos;s where I got tripped up for about a day and a half. If there&apos;s something wrong with the request body here, SendGrid API will return a &lt;code&gt;302&lt;/code&gt; error. That&apos;s right. &lt;code&gt;302&lt;/code&gt;. This error is very vague but I have been told (by StackOverflow) that it&apos;s meant for &lt;code&gt;GET&lt;/code&gt; requests only because you cannot re-try &lt;code&gt;POST&lt;/code&gt; requests.&lt;/p&gt;
&lt;p&gt;Don&apos;t go looking for this response in the SendGrid documentation, it&apos;s not there. Basically make sure you have submitted &lt;code&gt;string&lt;/code&gt;s and that they are populated. String interpolation is a good idea here.&lt;/p&gt;
&lt;p&gt;there&apos;s a long SO post about this which has some other suggestions, like don&apos;t break the &lt;code&gt;content&lt;/code&gt; up by using &lt;code&gt;\n&lt;/code&gt;s. If you are stuck on this one, and you&apos;ve tried the above without success, good luck.&lt;/p&gt;
&lt;p&gt;Okay, let&apos;s finish our &lt;code&gt;handlePostRequest&lt;/code&gt; function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!env ?? env.SENDGRID_API_KEY) {
  return Response.redirect(`${returnUrl}?success=false&amp;amp;reason=no-api-key`);
}

let emailResponse = await sendEmail(requestBody, env);

// continued below
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Submitting to the API&lt;/h2&gt;
&lt;p&gt;Some basic things here, the sendEmail is a fetch request, pretty straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function sendEmail(messageBody, env) {
  try {
    const email = await fetch(&quot;https://api.sendgrid.com/v3/mail/send&quot;, {
      method: &quot;POST&quot;,
      headers: {
        Authorization: `Bearer ${env.SENDGRID_API_KEY}`,
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
      body: JSON.stringify(messageBody),
    });
    return email;
  } catch (error) {
    return { status: 500, statusText: error };
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whatever the response is, we want to provide valuable feedback, and I haven&apos;t set up logging on CF functions yet so I want to pass those back to the client:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// continued from above
let emailResponse = await sendEmail(requestBody, env);

if (emailResponse.status &amp;gt; 299) {
  return Response.redirect(
    `${returnUrl}?success=false&amp;amp;reason=SendGrid%20API%20returned%20${emailResponse.statusText}%20(statusCode: ${emailResponse.status}))`
  );
}
return Response.redirect(`${returnUrl}?success=true`);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I enjoyed coming up with this flow. We&apos;re redirecting the user back to the sender URL with some query parameters that will allow us both to render a success/fail message, and to debug what&apos;s going on (as long as the user hasn&apos;t navigated away from the form).&lt;/p&gt;
&lt;h2&gt;CopyPasta Here&lt;/h2&gt;
&lt;p&gt;Putting that all together and you get this. Happy copypasting!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function onRequest(context) {
  const { request, env } = context;
  if (request.method === &quot;POST&quot;) {
    return await handlePostRequest(request, env);
  } else {
    return await handleDisallowedMethod();
  }
}

function composeRequest(formData, env) {
  const { email, firstname, surname, textarea } = formData;
  return {
    from: {
      email: env.SENDGRID_EMAIL_SENDER,
      name: &quot;my website&quot;,
    },
    replyTo: {
      email: `${email}`,
      name: `${firstname} ${surname}`,
    },
    subject: &quot;New message from my website&quot;,
    content: [
      {
        type: &quot;text/plain&quot;,
        value: `New message from ${firstname} ${surname} (${email}): &quot;${textarea}&quot;`,
      },
    ],
    personalizations: [
      {
        from: {
          email: env.SENDGRID_EMAIL_SENDER,
          name: &quot;my website (example.com)&quot;,
        },
        to: [
          {
            email: env.SENDGRID_EMAIL_RECIPIENT,
            name: &quot;Recipient&quot;,
          },
        ],
      },
    ],
  };
}

async function sendEmail(messageBody, env) {
  try {
    const email = await fetch(&quot;https://api.sendgrid.com/v3/mail/send&quot;, {
      method: &quot;POST&quot;,
      headers: {
        Authorization: `Bearer ${env.SENDGRID_API_KEY}`,
        &quot;Content-Type&quot;: &quot;application/json&quot;,
      },
      body: JSON.stringify(messageBody),
    });
    return email;
  } catch (error) {
    return { status: 500, statusText: error };
  }
}

async function handleDisallowedMethod() {
  return new Response(&quot;Object Not Found&quot;, {
    statusText: &quot;Object Not Found&quot;,
    status: 404,
  });
}

async function handlePostRequest(request, env) {
  const returnUrl = request.headers.get(&quot;referer&quot;);

  let formData = await readRequestBody(request);
  const requestBody = composeRequest(formData, env);f

  if (!env ?? env.SENDGRID_API_KEY) {
    return Response.redirect(`${returnUrl}?success=false&amp;amp;reason=no-api-key`);
  }

  let emailResponse = await sendEmail(requestBody, env);

  if (emailResponse.status &amp;gt; 299) {
    return Response.redirect(
      `${returnUrl}?success=false&amp;amp;reason=SendGrid%20API%20returned%20${emailResponse.statusText}%20(statusCode: ${emailResponse.status}))`
    );
  }
  return Response.redirect(`${returnUrl}?success=true`);
}

async function readRequestBody(request) {
  const { headers } = request;
  const contentType = headers.get(&quot;content-type&quot;);
  if (contentType.includes(&quot;application/json&quot;)) {
    const body = await request.json();
    return body;
  } else if (contentType.includes(&quot;form&quot;)) {
    const formData = await request.formData();
    let body = {};
    for (let entry of formData.entries()) {
      body[entry[0]] = entry[1];
    }
    return body;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Rendering the success / fail message&lt;/h2&gt;
&lt;p&gt;As a bit of icing on the cake, I have the following minimal JS in my Astro site to render success or fail messages to the user:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener(&quot;DOMContentLoaded&quot;, () =&amp;gt; {
  const formHandlerResponse = new URLSearchParams(window.location.search).get(
    &quot;success&quot;
  );
  const formHandlerReason = new URLSearchParams(window.location.search).get(
    &quot;reason&quot;
  );
  // return early
  if (!formHandlerResponse) return;

  // was it successful as a boolean instead of a string
  const isSuccessful = formHandlerResponse === &quot;true&quot;;

  // grab the original form
  const form = document.getElementById(&quot;contact-form&quot;);

  // ahh HTML templates. What joy.
  const submissionSuccessTemplate = document.querySelector(
    &apos;template[name=&quot;submission-success&quot;]&apos;
  );
  const submissionFailureTemplate = document.querySelector(
    &apos;template[name=&quot;submission-failure&quot;]&apos;
  );

  const formParent = form?.parentElement;

  formParent?.removeChild(form);

  formParent?.appendChild(
    isSuccessful
      ? submissionSuccessTemplate.content
      : submissionFailureTemplate.content
  );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;HTML Templates&lt;/h2&gt;
&lt;p&gt;With HTML templates I don&apos;t need to construct HTML out of JavaScript. Whilst these do have some limitations (good luck rendering the error message into a &lt;code&gt;&amp;lt;slot/&amp;gt;&lt;/code&gt; or whatever), they are a lot nicer to use in my opinion.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template name=&quot;submission-success&quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;Thank you for your message. We will be in touch shortly.&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&amp;lt;template name=&quot;submission-failure&quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;
      Sorry, something went wrong when we tried to submit your request. Please
      try again later.
    &amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Form Markup&lt;/h2&gt;
&lt;p&gt;For completion, here&apos;s the form markup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form
  id=&quot;contact-form&quot;
  method=&quot;POST&quot;
  action=&quot;/send-email&quot;
&amp;gt;
  &amp;lt;input
    required
    type=&quot;text&quot;
    name=&quot;firstname&quot;
    id=&quot;firstname&quot;
    placeholder=&quot;First name&quot;
  /&amp;gt;
  &amp;lt;input
    required
    type=&quot;text&quot;
    name=&quot;surname&quot;
    id=&quot;surname&quot;
    placeholder=&quot;Surname&quot;
  /&amp;gt;
  &amp;lt;input
    required
    type=&quot;email&quot;
    name=&quot;email&quot;
    id=&quot;email&quot;
    placeholder=&quot;Email address&quot;
  /&amp;gt;
  &amp;lt;textarea
    id=&quot;textarea&quot;
    placeholder=&quot;Message&quot;
    name=&quot;textarea&quot;
    rows=&quot;3&quot;
  &amp;gt;&amp;lt;/textarea&amp;gt;
  &amp;lt;input
    type=&quot;submit&quot;
    value=&quot;Send message&quot;
    /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Despite some challenges setting up Wrangler to accept my variables, crashing because of some unfathomable &lt;code&gt;clone&lt;/code&gt; problem, and sendGrid&apos;s insane &lt;code&gt;302&lt;/code&gt; status code, this was a good project. I appreciate the more lightweight feel to CF functions, and it has forced me to get to know SendGrid API a little better.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Sending Emails via SendGrid with Cloudflare Functions&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Serverless as a Competitive Advantage for your Business</title><link>https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/</guid><description>I gave a talk at Moar Serverless Conf 2022. If you’re still wondering what Serverless is, or if you’ve got friends and colleagues who are asking you, this talk will highlight some of the benefits.</description><pubDate>Sun, 21 Aug 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;This week I gave a talk at Moar Serverless Conf 2022. The subject was aimed at helping people who are reticent about serverless, or who love it but struggle to convince other people to try it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=S3fqGuzpgMg&amp;amp;feature=emb_imp_woyt&quot;&gt;Watch the video here&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The 4 benefits I talked about are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No server management&lt;/li&gt;
&lt;li&gt;Inherent automatic scaling&lt;/li&gt;
&lt;li&gt;Consumption based pricing&lt;/li&gt;
&lt;li&gt;Faster product iteration&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;No Server Management &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/#no-server-management&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I once had an error at a place I worked that stopped all of our applications from deploying, and which took around a week for four people to resolve. It was that the containers were running on Ubuntu 16, which became unsupported by the infrastructure.&lt;/p&gt;
&lt;p&gt;If we had used Serverless, the problem would have been minimised because we don&apos;t care what OS the application is running on. So we can be more flexible about dependency upgrades.&lt;/p&gt;
&lt;h2&gt;Automatic scaling &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/#automatic-scaling&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I talked about how organisations will usually have around 2 replicasets of their application running continuously, and have autoscaling to maybe 4. But that traffic spikes could easily go beyond this, leading to Error 429messages.&lt;/p&gt;
&lt;p&gt;In a serverless environment, you just watch the numbers roll up, knowing that your infrastructure will scale to the amount of requests.&lt;/p&gt;
&lt;h2&gt;Consumption based pricing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/#consumption-based-pricing&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I had two numbers on the screen: $2,000 and $0.86. The first figure is what I guessed one company I worked at once was paying for just one of it&apos;s applications in one environment every month. They had 4 environments, and 13 applications, and so the monthly cost would be huge.&lt;/p&gt;
&lt;p&gt;In contrast, I shared that I got my first AWS bill last month and it was $0.86. This is after 2 years of use.&lt;/p&gt;
&lt;p&gt;I admitted that it wasn&apos;t a fair comparison; I mostly have small blogs which don&apos;t get a lot of requests, but I was just amazed at the difference in costs.&lt;/p&gt;
&lt;h2&gt;Faster product iteration &lt;a href=&quot;https://deliciousreverie.co.uk/posts/serverless-competitive-advantage-business/#faster-product-iteration&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I said that since developers don&apos;t have to worry about every part of the stack, they can focus on business deliverables. That means the business can go faster and beat the competition to market, instead of being late to the party.&lt;/p&gt;
&lt;p&gt;I concluded by saying Serverless isn&apos;t a rose garden. It requires that the application is specifically built for such an environment. This makes adopting serverless difficult.&lt;/p&gt;
&lt;p&gt;So developers need to start with serverless, not try to adopt it later.&lt;/p&gt;
&lt;p&gt;One way of getting started with Serverless quickly is to take an existing open source application and deploy it. That allows you to see the potential and how certain problems have already been solved.&lt;/p&gt;
&lt;p&gt;I advised that developers take Webiny for a spin, because it has all of the benefits of Serverless without the cost of building something yourself.&lt;/p&gt;
&lt;p&gt;It was great to speak at an event with so many other good people I know and respect.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Serverless as a Competitive Advantage for your Business&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Should I move from WordPress to code?</title><link>https://deliciousreverie.co.uk/posts/should-i-move-from-wordpress-to-code/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/should-i-move-from-wordpress-to-code/</guid><description>I&apos;m getting asked this question a lot lately, so I made a few charts that I hope explains the choices people face when they are thinking about switching, and the effort that might take.</description><pubDate>Thu, 24 Oct 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I&apos;m getting asked this question a lot lately, so I made a few charts that I hope explains the choices people face when they are thinking about switching, and the effort that might take. I hope it helps someone.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Web development is a funny term isn&apos;t it? It encompasses a wide section of people who build things with the web, and they may or may not be technical.&lt;/p&gt;
&lt;p&gt;But then I suppose something similar could be said of people who speak a language: I speak a few, but none of which I could confidently say I&apos;m fluent.&lt;/p&gt;
&lt;p&gt;So too web development is a ~spectrum~ gradient. Along the gradient are people who code considerably well in JavaScript, and those who build things with off-the-shelf themes.&lt;/p&gt;
&lt;p&gt;So I thought I&apos;d draw up some comparisons for you.&lt;/p&gt;
&lt;h2&gt;Language difficulty charts&lt;/h2&gt;
&lt;p&gt;You may have seen something similar to these graphs before: for each language you learn, there&apos;s a learning &lt;em&gt;curve&lt;/em&gt;: sometimes it&apos;s relatively easy to begin with and gets harder as you learn more about a language.&lt;/p&gt;
&lt;p&gt;This one below is for an English speaker learning Chinese. As you see, it starts quite difficult. Tonal languages are challenging for English speakers. Then there&apos;s a dip in the middle, which shows that once you&apos;ve got the idea of tones, you realise the grammar is simpler when compared to English: there&apos;s no tenses which mutate word endings, there&apos;s no masculine / feminine dichotomy to contend with. But then as you learn those things you realise there&apos;s a lot of challenges with context, and communicating time. There are more than 10 different definitions for the partial le (了). So towards the end of your journey learning Chinese, you have a steep rise in difficulty again.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Code learning difficulty chart&lt;/h2&gt;
&lt;p&gt;I think learning to code can be charted similarly. It starts off rather difficult, but then gets easier with time. You have trouble figuring out what all those chevrons mean, what attributes different elements have, and when to self-close and when not to.&lt;/p&gt;
&lt;p&gt;Then you pick up CSS and realise it&apos;s just a selector, a key and a value. Not to say there aren&apos;t challenges: learning how the cascade works is likely going to be a major &quot;eureka!&quot; moment for everyone.&lt;/p&gt;
&lt;p&gt;At least, that has been my experience.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;But where does WordPress sit on this gradient?&lt;/p&gt;
&lt;p&gt;Here.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To start with, it&apos;s easy peasy. Run the installer, grab your pagebuilder plugin, and start dropping boxes onto the page. Add more plugins for forms and other functionality, for Google Analytics, etc etc etc.&lt;/p&gt;
&lt;p&gt;Sooner or later youre going to want something that all of the plugins you have access to don&apos;t do. So you do a web search. You find this one dodgy plugin from seven years ago that you have to pay for in Bitcoin.&lt;/p&gt;
&lt;p&gt;Two weeks later you find that someone has defaced your website. You delete the defacement. It comes back.&lt;/p&gt;
&lt;p&gt;You&apos;ve been hacked.&lt;/p&gt;
&lt;p&gt;How do you un-hack a WordPress website? Good luck finding a plugin for that, if you can even install one by this point. Two days later the client calls you saying people can&apos;t access the website because it&apos;s behind a red screen. You&apos;ve been blacklisted from search engines.&lt;/p&gt;
&lt;p&gt;Now the learning curve is so steep it&apos;s almost impossible. And people are shouting at you to fix it.&lt;/p&gt;
&lt;p&gt;This is the point that people often start questioning their technology choices.&lt;/p&gt;
&lt;p&gt;Imagine if you&apos;d questioned them sooner.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Should I move from WordPress to code?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Should I use ternary or the logical and operator?</title><link>https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/</guid><description>Templating with JSX in React is easy ... until it&apos;s not. Recently a colleague recommended I use the logical and operator instead of a ternary. However once we&apos;d dug into it a little, we found these operators do very different things ... </description><pubDate>Thu, 06 May 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Templating with JSX in React is easy ... until it&apos;s not. Recently a colleague recommended I use the logical and operator instead of a ternary. However once we&apos;d dug into it a little, we found these operators do very different things ...&lt;/p&gt;
&lt;p&gt;Quite often we follow this pattern for brevity, and there&apos;s good value doing it if there&apos;s only one variable at play:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{  isLoggedIn &amp;amp;&amp;amp; &amp;lt;SomeComponent /&amp;gt;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This avoids us from having to write something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{  isLoggedIn ? &amp;lt;SomeComponent /&amp;gt; : null;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which is totally redundant here, since if it&apos;s value is false, it won&apos;t return the component.&lt;/p&gt;
&lt;p&gt;However, when there&apos;s a couple of things going on you might find it doing something unexpected:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  formErrors.likesPotatoes ||
  formErrors.likesBananas ||
  formErrors.likesCake ? (
    &amp;lt;NotificationMessage icon=&quot;alert&quot; status=&quot;error&quot;&amp;gt;
      &amp;lt;p&amp;gt;
        Please ensure that all the required questions have been answered before
        proceeding.
      &amp;lt;/p&amp;gt;
    &amp;lt;/NotificationMessage&amp;gt;
  ) : null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is not equivalent to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  formErrors.likesPotatoes ||
    formErrors.likesBananas ||
    (formErrors.likesCake &amp;amp;&amp;amp; (
      &amp;lt;NotificationMessage icon=&quot;alert&quot; status=&quot;error&quot;&amp;gt;
        &amp;lt;p&amp;gt;
          Please ensure that all the required questions have been answered
          before proceeding.
        &amp;lt;/p&amp;gt;
      &amp;lt;/NotificationMessage&amp;gt;
    ));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the ternary operator (&lt;code&gt;isTrue ? dothis : dothat&lt;/code&gt;), our &lt;code&gt;&amp;lt;NotificationMessage/&amp;gt;&lt;/code&gt; will show when any of the conditions are met. The logical &lt;code&gt;AND&lt;/code&gt; (&lt;code&gt;isTrue &amp;amp;&amp;amp; dothat&lt;/code&gt;) will only show the component if all of the conditions are met.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;h2&gt;The Difference between the logical AND and ternaries &lt;a href=&quot;https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/#the-difference-between-the-logical-and-and-ternaries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ternaries work similar to the if operator. So it short circuits (closes off before any other variable is assessed), and returns true if any of the values are true.&lt;/p&gt;
&lt;p&gt;On the other hand, the logical AND operator returns true &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND&quot;&gt;only if and only if all of its operands are true&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In our case, when checking for form errors, we want to show a notification if any of the fields have an error. So the ternary is the way to go.&lt;/p&gt;
&lt;h2&gt;Alternative 1: abstract it &lt;a href=&quot;https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/#alternative-1:-abstract-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There is another way of handling this situation where you could use the logical &lt;code&gt;AND&lt;/code&gt;: chain those errors in a variable before returning the JSX:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hasError =
  formErrors.likesPotatoes || formErrors.likesBananas || formErrors.likesCake;
return (
  &amp;lt;&amp;gt;
    {hasError &amp;amp;&amp;amp; (
      &amp;lt;NotificationMessage icon=&quot;alert&quot; status=&quot;error&quot;&amp;gt;
        &amp;lt;p&amp;gt;
          Please ensure that all the required questions have been answered
          before proceeding.
        &amp;lt;/p&amp;gt;
      &amp;lt;/NotificationMessage&amp;gt;
    )}
  &amp;lt;/&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Alternative 2: Wrap it &lt;a href=&quot;https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/#alternative-2:-wrap-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My friend Warrick Hill mentioned that you could also wrap the options in brackets to ensure they get evaluated together and therefore don&apos;t short circuit. This is how mathematical bracket operators work, where everything inside the brackets gets evaluated first, for example &lt;code&gt;(2 \* 3) + 2 = 8&lt;/code&gt; but &lt;code&gt;2 * (3 + 2) = 10&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return (
  &amp;lt;&amp;gt;
    {(formErrors.likesPotatoes ||
      formErrors.likesBananas ||
      formErrors.likesCake) &amp;amp;&amp;amp; (
      &amp;lt;NotificationMessage icon=&quot;alert&quot; status=&quot;error&quot;&amp;gt;
        &amp;lt;p&amp;gt;
          Please ensure that all the required questions have been answered
          before proceeding.
        &amp;lt;/p&amp;gt;
      &amp;lt;/NotificationMessage&amp;gt;
    )}
  &amp;lt;/&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although Warrick points out that this is harder to spot than the alternative #1 above.&lt;/p&gt;
&lt;h2&gt;Thanks &lt;a href=&quot;https://deliciousreverie.co.uk/posts/should-i-use-ternary-or-logicaland-in-react/#thanks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks to Aimable N and Chris Geary (as well as Warrick) for their help with this article.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Should I use ternary or the logical and operator?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Should I Use Gatsby or Next.js For My Next Project?</title><link>https://deliciousreverie.co.uk/posts/should-use-gatsby-or-nextjs/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/should-use-gatsby-or-nextjs/</guid><description>Originally written for the Webiny blog and was written at a time when Gatsby was experiencing a bit of a resurgence. By writing it, I was hoping to tap into this renewed interest and provide value by highlighting the differences and advantages to each tool.</description><pubDate>Fri, 23 Sep 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Developers tend to get very passionate about their tools. And rightly so: without the best tools for the job at hand, we couldn&apos;t achieve our objectives, make something awesome, or build a better world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://reactjs.org/&quot;&gt;React&lt;/a&gt; is still a major player in terms of frontend development. There&apos;s currently a competition of sorts going on between the two React heavyweight frameworks &lt;a href=&quot;https://nextjs.org/&quot;&gt;Next.js&lt;/a&gt; and &lt;a href=&quot;https://www.gatsbyjs.com/&quot;&gt;Gatsby&lt;/a&gt;. Which one is better? Which should you learn? Is Gatsby better, or Next.js, for building your next project?&lt;/p&gt;
&lt;p&gt;At Webiny, we&apos;re interested in providing developers a backend Headless CMS to compliment your frontend, so it&apos;s of deep interest to us which tools are popular. Knowing which ones you&apos;re using ... even which ones you are preferring, influences our product decisions. We think about it when we talk about marketing. And we consider it when we&apos;re planning what we build into our open source CMS.&lt;/p&gt;
&lt;p&gt;So we decided to do a bit of critical thinking about whether Next.js or Gatsby is better. We came up with these criteria to match them against. Even so, we&apos;re going to leave it up to you as to which one you&apos;re going to use!&lt;/p&gt;
&lt;h2&gt;Server Generation and Configuration&lt;/h2&gt;
&lt;p&gt;Generating React code, or any JavaScript, when an application is built, is a critical metric when it comes to frontend frameworks like Gatsby and Next.js. But we don&apos;t think it&apos;s speed that&apos;s critical. It&apos;s certainly important, but it&apos;s one of those metrics that the developers of these frameworks are acutely aware of, so they&apos;re going to make sure they keep making advancements in this area.&lt;/p&gt;
&lt;p&gt;What&apos;s more interesting is what APIs you have access to on server generation. Does the tool make it easier or more difficult to do what you need to? That&apos;s what we&apos;re assessing here.&lt;/p&gt;
&lt;p&gt;Whilst Next.js allows access to customize headers, page initialization, add rewrites and redirects with it&apos;s &lt;a href=&quot;https://nextjs.org/docs/api-reference/next.config.js/introduction&quot;&gt;config&lt;/a&gt; file and custom &lt;a href=&quot;https://nextjs.org/docs/advanced-features/custom-app&quot;&gt;_App.js&lt;/a&gt; component, Gatsby has a &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/config-files/gatsby-config/&quot;&gt;config&lt;/a&gt; file, &lt;a href=&quot;https://www.gatsbyjs.com/docs/custom-html/&quot;&gt;a custom html.js document&lt;/a&gt; (which is similar to Next.js). It also has files to directly access the &lt;a href=&quot;https://www.gatsbyjs.com/docs/recipes/pages-layouts/#project-structure&quot;&gt;server-rendered application&lt;/a&gt; and the &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/config-files/gatsby-browser/&quot;&gt;hydrated application in the browser&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Gatsby certainly has a wider variety of configuration open to developers here. But that can also be daunting or unnecessarily complex for someone who just wants to build with React but also have static generation.&lt;/p&gt;
&lt;p&gt;Also, sometimes it&apos;s not clear what the files are for. For example, the &lt;a href=&quot;https://www.gatsbyjs.com/docs/reference/config-files/gatsby-node/&quot;&gt;gatsby-node.js&lt;/a&gt; file isn&apos;t specifically for APIs that run in the server environment (which would be using Node.js), instead, this is where the internal node transformation happens, allowing you to create dynamic pages and custom GraphQL resolvers to your application.&lt;/p&gt;
&lt;p&gt;Admittedly, once you know that, it&apos;s great to have access to all of these APIs to customize your data. But we&apos;ll get to that a bit more later.&lt;/p&gt;
&lt;h2&gt;Documentation&lt;/h2&gt;
&lt;p&gt;Whether Gatsby or Next.js documentation is good or bad seems highly subjective. But sometimes documentation can inadvertently hide things developers need to know under generalizations or by accidentally missing things out. Sometimes an API badly explained is worse than not having it documented at all.&lt;/p&gt;
&lt;p&gt;We all know this, which is why we looked at documentation as a marker on whether you should choose Next.js or Gatsby to build your next project.&lt;/p&gt;
&lt;p&gt;Whilst Next.js documentation has a cleaner, focused look, Gatsby&apos;s is busier with a 3-column layout. Though sometimes that&apos;s a good thing. Developers probably won&apos;t get as lost contextually in the Gatsby docs as they might in Next.js.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;We generally prefer the darker code blocks and higher contrast on the Next.js website. But it could be because we&apos;re just getting old and our eyes aren&apos;t as good as they used to be.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Next.js&apos; strengths come out clearly in it&apos;s focused approach towards lower-level APIs such as the getStaticProps() and getServerSideProps() functions, and these are explained very well. But there are also some things that are not mentioned except succinctly in an &lt;a href=&quot;https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features&quot;&gt;&quot;unsupported features&quot; section&lt;/a&gt;. This can be frustrating when you find out you&apos;ve built your application to use a certain feature, only to realize it&apos;s not supported in the environment you intended to deploy to.&lt;/p&gt;
&lt;h2&gt;Routing&lt;/h2&gt;
&lt;p&gt;Both frameworks take advantage of file-based routing. &lt;a href=&quot;https://nextjs.org/docs/routing/dynamic-routes&quot;&gt;Next.js&lt;/a&gt; and &lt;a href=&quot;https://www.gatsbyjs.com/docs/how-to/routing/creating-routes/#dynamic-and-authenticated-routing&quot;&gt;Gatsby&lt;/a&gt; have the ability to define dynamic routes as well, but Gatsby doesn&apos;t clearly explain how this works, perhaps assuming you&apos;re only looking for this feature if you&apos;ve already used Next.js.&lt;/p&gt;
&lt;p&gt;What is great about Next.js is the ability to create &lt;a href=&quot;https://nextjs.org/docs/api-routes/introduction&quot;&gt;API Routes&lt;/a&gt;. This could be really useful in a lot of situations, especially if you have a sprawling REST API endpoint and want a minimal set of data returned from it, you could massage that data down to only the content you need on your frontend. In fact, Next.js has it&apos;s &lt;a href=&quot;https://github.com/vercel/micro&quot;&gt;own express-like server-side framework called Micro&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This turns Next.js into a truly isomorphic application, which is meant to have components server side and on the client, working in tandem to produce a single application.&lt;/p&gt;
&lt;p&gt;On the other hand, Gatsby seems to keep these concerns clearly separated, even with their own &lt;a href=&quot;https://www.gatsbyjs.com/products/cloud/functions&quot;&gt;Gatsby Functions&lt;/a&gt;, the emphasis is on a separate backend that communicates with the hydrated frontend application.&lt;/p&gt;
&lt;p&gt;Do you like to mix these concerns? Or keep them separate? It depends slightly on what kind of developer you are, and what kind of project you&apos;re building, as to which you&apos;ll prefer from this point of view.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Gatsby and Next.js are two very different tools with unique value propositions: either low-level tools to provide fundamental APIs and free developers from unnecessary tooling, or an ecosystem full of plugins of varying quality, together with a data layer to stitch your content together.&lt;/p&gt;
&lt;p&gt;As we said at the start, this isn&apos;t a full roundup of all of their features. Maybe what you&apos;re building needs something more specific like &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;, &lt;a href=&quot;https://nuxtjs.org/&quot;&gt;Nuxt&lt;/a&gt; or &lt;a href=&quot;https://kit.svelte.dev/&quot;&gt;SvelteKit&lt;/a&gt;. We only know what we&apos;ve encountered when building our starters for Next.js and Gatsby.&lt;/p&gt;
&lt;p&gt;Oh we didn&apos;t tell you? We&apos;ve built 1-click deploy starters for Webiny headless CMS.&lt;/p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https://github.com/webiny/nextjs-starter-webiny&quot;&gt;1-click starter for Next.js and Webiny Headless CMS on Vercel&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;👉 &lt;a href=&quot;https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/webiny/gatsby-starter-webiny&quot;&gt;1-click starter for Gatsby and Webiny Headless CMS on Gatsby Cloud&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Make sure you check those out. Maybe it&apos;ll help you make your mind up ... should you use Gatsby or Next.js? The choice is ultimately yours.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Should I Use Gatsby or Next.js For My Next Project?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Start and end with the people</title><link>https://deliciousreverie.co.uk/posts/start-and-end-with-the-people/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/start-and-end-with-the-people/</guid><description>Professional software development at an enterprise scale is inherently, and at every level, social. How does comprehension of this fact help teams to perform better?</description><pubDate>Sat, 19 Oct 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Professional software development at an enterprise scale is inherently, and at every level, social. How does comprehension of this fact help teams to perform better?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Who are the people who matter most to the product your team produces? I&apos;m sure you&apos;re probably thinking about this in terms of &lt;em&gt;end&lt;/em&gt; users: the people who spend time on the platform, working their way through carefully crafted workflows you&apos;ve been crafting.&lt;/p&gt;
&lt;p&gt;The people who use our products are the ones we obsess over the most. After all the chances are that they play the most significant role in generating the revenue our organisations need.&lt;/p&gt;
&lt;p&gt;However, in any organisation there are &lt;em&gt;other&lt;/em&gt; groups of people we ought to give significant consideration to. When we acknowledge and seek to meet the needs of these other groups I think we are the most effective we can be as a software team.&lt;/p&gt;
&lt;p&gt;Two of the most significant of these are those higher up the chain (stakeholders) and those who are closer to the product (engineers). There are others in the wider team too (power users) that we should also give consideration to.&lt;/p&gt;
&lt;h2&gt;Stakeholders&lt;/h2&gt;
&lt;p&gt;Are stakeholders users of our product? It would be a mistake to see them as such, even if they use it themselves. Their unique perspective puts them in a responsible position towards users. However, their recommendations, suggestions and feature requests need to be validated and researched if the actual needs of stakeholders are to be met.&lt;/p&gt;
&lt;p&gt;What do I mean by that?&lt;/p&gt;
&lt;p&gt;Imagine a stakeholder requests a feature, which is added straight to the backlog. The feature is built and shipped, the stakeholder&apos;s requirements are satisfied. But the feature proves to be a waste of time.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Without due diligence is done beforehand, we are not meeting the &lt;em&gt;real&lt;/em&gt; needs of stakeholders. After all, we didn&apos;t ask the most important question before any further work was done: &quot;&lt;em&gt;why&lt;/em&gt; do you want this feature?&quot;. Anything other than &quot;I think it would be good&quot; is an acceptable answer here.&lt;/p&gt;
&lt;p&gt;Without that thought process, we could build a feature that nobody will use. Or we could build something that doesn&apos;t solve the problem the stakeholder was trying to address.&lt;/p&gt;
&lt;p&gt;So ask them, what is it you&apos;re trying to achieve?&lt;/p&gt;
&lt;p&gt;Even after we&apos;re clear about what is being requested, that&apos;s not the point it should reach the backlog; we would only be satisfying the real needs of stakeholders by doing proper due diligence.&lt;/p&gt;
&lt;p&gt;We might need to do further research about whether it&apos;s a good fit for our product, whether competitors have already implemented it or not, and why, whether users think it&apos;s a feature they could benefit from, and how much time might it take to build?&lt;/p&gt;
&lt;p&gt;The main idea behind all of these is principally, &quot;is it something that will add value to the product&quot; and then, &quot;is it worth the cost of implementation&quot;?&lt;/p&gt;
&lt;p&gt;Stakeholders exist to improve the value of the product. If they&apos;re not achieving that, they&apos;re not going to be fulfilled. That means that knowing the implications of what they&apos;ve asked for is a key driver to their decision making.&lt;/p&gt;
&lt;h2&gt;Team members&lt;/h2&gt;
&lt;p&gt;Ensuring the success of people who use what we&apos;re building as well as those who are stakeholders in the product should be as important to us as the success of the individual members of the team involved in constructing it.&lt;/p&gt;
&lt;p&gt;How are &lt;em&gt;they&lt;/em&gt; doing? Are they having a good day, a good week, a good experience working for you?&lt;/p&gt;
&lt;p&gt;It&apos;s only when we help facilitate them being able to find their place within the team that we get the most out of them.&lt;/p&gt;
&lt;p&gt;That means that if they are disengaged, its our job to find out why that might be the case. Often it boils down to three reasons.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Tasks are too challenging&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this case, the person might benefit from some extra coaching or support to achieve objectives. Their backlog could be cleared to help them focus if they are easily distracted, or tasks can perhaps be broken down to improve focus. The size of a ticket matters less about it&apos;s percieved difficulty than it is possible to do for the one who fulfils it. So customise tickets: write smaller tickets for those less able to focus, larger ones for those who can.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Personal problems&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;How well do we know what&apos;s going on in the lives of those we are responsible for? If there is no &quot;idle&quot; conversation, then how will you know if a bereavement has affected them? If they are having issues with a colleague? If they are struggling with impostor syndrome?&lt;/p&gt;
&lt;p&gt;Get to know your people. Being an effective leader means being someone who represents the company&apos;s consideration for them when they&apos;re going through a rough patch. You&apos;ll get so much more out of them in terms of years of service as well as productivity if you make a friend instead of just a colleague.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lack of clear direction&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We don&apos;t want to be seen as treating people like they&apos;re infants, but often what someone perceives as clear direction is much more explicit that we might expect. Have we clarified what is expected of them for this task? Have we asked them to outline their plan verbally to us? (often this is a really good way to check understanding, a simple &quot;how do you think you&apos;re going to approach it?&quot; is enough).&lt;/p&gt;
&lt;p&gt;More importantly than either of the above questions is &quot;do they feel comfortable approaching us if they &lt;em&gt;don&apos;t&lt;/em&gt; understand what is expected of them?&quot;&lt;/p&gt;
&lt;p&gt;If your response to their questions give even a slight hint that you&apos;re expecting them to already comprehend fully your requirements, then you&apos;re doing it wrong. There should be no hesitation on their part to ask you for more clarifications, more detail, or to challenge anything about the work you have scheduled for them.&lt;/p&gt;
&lt;p&gt;Otherwise it is likely that the delivery will suffer, and so will their confidence, which could lead to other significant issues and underperformance.&lt;/p&gt;
&lt;p&gt;Knowing our team personally and making sure individuals are comfortable approaching us for clarification is a big step to creating a team that is as productive as it can be.&lt;/p&gt;
&lt;h2&gt;The wider business&lt;/h2&gt;
&lt;p&gt;So far we&apos;ve looked at individuals within the business. But are the people who are impacted by changes in our product ever involved before we release new features?&lt;/p&gt;
&lt;p&gt;I have found there are often key people within a business, whether that&apos;s power users or that one technical person in the 1st line support team, who have a deep understanding of the product. Do we consider them?&lt;/p&gt;
&lt;p&gt;Even if we just tell them what&apos;s coming up, it can empower their decision making and the way they give support to others significantly. If we also ask them for involvement in scoping features, or even suggesting new ones, we&apos;ll tap into a potential goldmine of new ideas that can inject more revenue into a business from unexpected angles.&lt;/p&gt;
&lt;h2&gt;Start and end with the people&lt;/h2&gt;
&lt;p&gt;The people are what make up everything about our business. Look holistically at our product. It&apos;s definition might be a piece of software, but it&apos;s the result of interactions between people. When we focus on that instead, zooming out of implementation details, and instead focusing on what can do to fulfil stakeholders, generate effective teams, and involve the wider business, we will build things that are wanted, needed and enjoyed. By all involved.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Start and end with the people&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>SVG Animation on Hover with GreenSock</title><link>https://deliciousreverie.co.uk/posts/svg-animation-on-hover-with-greensock/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/svg-animation-on-hover-with-greensock/</guid><description>One thing I&apos;ve tried to do more of is to use animations on frontend projects I&apos;ve been involved with. GreenSock animation library is a great way of standardising and improving on animations that otherwise wouldn&apos;t be available on all browsers.</description><pubDate>Sun, 21 May 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;One thing I&apos;ve tried to do more of is to use animations on frontend projects I&apos;ve been involved with. GreenSock animation library is a great way of standardising and improving on animations that otherwise wouldn&apos;t be available on all browsers.&lt;/p&gt;
&lt;p&gt;Animations add another dimension to your projects that helps users and can provide either an extra bit of feedback, extra character, and even increase conversions.&lt;/p&gt;
&lt;p&gt;I really love GreenSock for it&apos;s performance and range of animations. I particularly like their modular approach to the library. For example, if you have something relatively straightforward in mind, you can just use the &quot;Tween Lite&quot; plugin, which is 27kb minified. If you need more flexibility with timelines, you can add Timeline Lite, a further 12kb.&lt;/p&gt;
&lt;p&gt;This means that you&apos;re not tied to a monolithic platform that covers all bases. You can customise and work towards a performance budget whilst still using some great features.&lt;/p&gt;
&lt;p&gt;GreenSock also works really well with ScrollMagic to make rich interactive experiences. It&apos;s often the combination of these libraries that result in great experiences that are often featured on the &lt;a href=&quot;https://www.awwwards.com/&quot;&gt;Website Awwards&lt;/a&gt; site.&lt;/p&gt;
&lt;h2&gt;Example &lt;a href=&quot;https://deliciousreverie.co.uk/posts/svg-animation-on-hover-with-greensock/#example&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a recent project, I wanted to add some hover effects to a UI element that made it seem as if it was almost a fluid change of state. The element was contained in an SVG shape with 2 layers, the background and the graphic.&lt;/p&gt;
&lt;h2&gt;A fluid-feeling interaction &lt;a href=&quot;https://deliciousreverie.co.uk/posts/svg-animation-on-hover-with-greensock/#a-fluid-feeling-interaction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I found that there are a few caveats when working with SVGs. The first is how to scale from the center of the animation. To do that you can add &lt;code&gt;transformOrigin: 50% 50%&lt;/code&gt; to the properties you&apos;re applying. That will find the center of the SVG element, and animate from there.&lt;/p&gt;
&lt;p&gt;Initially, to animate this I tried to create one timeline that targeted both elements, but this resulted in an effect that looked artificial, since the animations didn&apos;t overlap each other. One scale effect was applied to one element. It was only after that that the other scale effect started.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var connectCircles = new TimelineMax();

connectCircles.staggerTo($(this).find(&quot;.cloud, .microphone, .handset&quot;), 0.3, {
          scaleX: 1.2,
          scaleY: 1.2,
          ease: Elastic.easeOut,
      transformOrigin:&quot;50% 50%&quot;
          }).staggerTo($(this).find(&quot;.disc&quot;), 0.6, {
          scaleX: 1.2,
          scaleY: 1.2,
          ease: Elastic.easeOut,
      transformOrigin:&quot;50% 50%&quot;
          })
        };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output: https://codepen.io/endymion1818/pen/dWQevw&lt;/p&gt;
&lt;p&gt;To resolve this issue, I split the functions into separate timelines that allowed them to run separately:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var connectCircles = new TimelineMax();
var connectShapes = new TimelineMax();

connectCircles.staggerTo($(this).find(&quot;.cloud, .microphone, .handset&quot;), 0.3, {
          scaleX: 1.2,
          scaleY: 1.2,
          ease: Elastic.easeOut,
      transformOrigin:&quot;50% 50%&quot;
          });

      // second timeline
      connectShapes.staggerTo($(this).find(&quot;.disc&quot;), 0.6, {
          scaleX: 1.2,
          scaleY: 1.2,
          ease: Elastic.easeOut,
      transformOrigin:&quot;50% 50%&quot;
          })
        };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That way, when we run the animation, the two timelines run independently from each other:&lt;/p&gt;
&lt;p&gt;https://codepen.io/endymion1818/pen/xgEYqG&lt;/p&gt;
&lt;p&gt;Getting the timing right in animations is one of the hardest things, and takes the longest amount of time. It took me quite a bit of experimentation and asking for feedback to arrive at the duration of each transition so that it appeared that the effects were interacting with each other in a natural way.&lt;/p&gt;
&lt;p&gt;A lot of animation is fooling the eye into thinking it&apos;s real. Our brains recognise natural interactions which follow our experience of physical reality, for example, the way a water droplet responds when you touch it. But if you get it wrong by a fraction, it looks strange or unfamiliar, and it can have an adverse effect on your audience.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/svg-animation-on-hover-with-greensock/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve had so much fun with this you wouldn&apos;t believe. I am really excited about animations and look forward to working with GreenSock and the Web Animations API more in the future.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:SVG Animation on Hover with GreenSock&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Content-Author friendly APIs with Strapi</title><link>https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/</guid><description>I&apos;ve worked in a few different capacities around the web, and can see the value of interfaces that those writing content for the web feel comfortable using. I&apos;m also becoming more comfortable with JavaScript, and want to find alternatives to the PHP tools I&apos;ve been using. On that journey, I discovered Strapi. </description><pubDate>Fri, 08 Jun 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;ve worked in a few different capacities around the web, and can see the value of interfaces that those writing content for the web feel comfortable using. I&apos;m also becoming more comfortable with JavaScript, and want to find alternatives to the PHP tools I&apos;ve been using. On that journey, I discovered Strapi.&lt;/p&gt;
&lt;p&gt;When I think of content authors, I&apos;m specifically thinking about non-technical individuals who don&apos;t want to get involved in writing HTML. For these people, making or editing a Markdown file might be a disagreeable process. This is simply because they need to be in an envinronment that allows them to focus on their current task — writing an article, updating page content, publishing a job advert - without distraction. Or at least, with minimal distraction.&lt;/p&gt;
&lt;p&gt;I am sure this is one reason WordPress has gained the community it has:- it&apos;s easy to use, and makes the content author feel informed and empowered without them feeling like the software is getting in the way.&lt;/p&gt;
&lt;p&gt;The challenge for me has become to find some software - preferably open-source, definitely JavaScript - that can be a suitable environment for these kind of authors.&lt;/p&gt;
&lt;p&gt;And I think Strapi could be a very good fit indeed.&lt;/p&gt;
&lt;h2&gt;How Strapi Works for Content Authors &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#how-strapi-works-for-content-authors&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The 3 main requirements I had when analysing this project for a content authors&apos; perspective were:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easy to write &amp;amp; edit posts&lt;/li&gt;
&lt;li&gt;easy to restrict and enable access and authorship&lt;/li&gt;
&lt;li&gt;content authors feel in control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;m going to cover these in more detail below.&lt;/p&gt;
&lt;h3&gt;Writing and Editing Posts &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#writing-and-editing-posts&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Logging in and authoring content in Strapi works very much like any other content platform. There&apos;s a left sidebar which you can use to navigate different types of content, and fields that you fill out that are clearly labeled, functional and give suitable feedback when you&apos;re filling them out, or saving posts, or just navigating around.&lt;/p&gt;
&lt;h3&gt;Author Roles and Permissions &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#author-roles-and-permissions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;New authors can be defined by administrators, and access can be given to specific areas of the API.&lt;/p&gt;
&lt;p&gt;This allows us a key feature that I&apos;ve seen being particularly useful in larger applications: authors can write articles that can be reviewed before publication.&lt;/p&gt;
&lt;h3&gt;Authors Feel In Control &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#authors-feel-in-control&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The thing that I felt conflicted most about WordPress was that it gave too much control to authors. The ability to add plugins and change the site&apos;s appearance made it a minefield for a developer.&lt;/p&gt;
&lt;p&gt;But WordPress made a deliberate choice to do that. WordPress&apos; aim is to &quot;democratise publishing&quot; so that anyone can have a blog or personal website.&lt;/p&gt;
&lt;p&gt;There&apos;s always a balance to be struck here: too restrictive and authors will go elsewhere, too open and you end up with a mess of a site that can cost thousands to fix.&lt;/p&gt;
&lt;p&gt;Strapi has a plugin ecosystem, which I hope will grow. Through their marketplace (which you can&apos;t miss, it&apos;s heavily promoted within the backend), you can add analytics tracking and other functionality, or purchase plugins which extend Strapi.&lt;/p&gt;
&lt;p&gt;This allows content authors and business owners to feel more in control of their project. Having these plugins means they&apos;re not totally dependent on others when it comes to extending their Strapi site.&lt;/p&gt;
&lt;p&gt;It makes authors feel that the site belongs to them and not their developer.&lt;/p&gt;
&lt;p&gt;But, of course, it has to work for developers as well...&lt;/p&gt;
&lt;h2&gt;How Strapi Works For Developers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#how-strapi-works-for-developers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For Strapi to work for those with a development background, I am looking for the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;easy API authorship&lt;/li&gt;
&lt;li&gt;extensible code&lt;/li&gt;
&lt;li&gt;ease of entry for newer developers&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;API Authorship &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#api-authorship&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;API authorship can be done in 2 ways with Strapi. Either in the GUI or in the command line. This, I think, is a master stroke.&lt;/p&gt;
&lt;p&gt;Independently-minded content authors can define or destroy their own content fields, and entire types as they wish, in a GUI that gives them adequate feedback about what&apos;s happening.&lt;/p&gt;
&lt;p&gt;Conversely, APIs can be set by the developer in a JavaScript object, without having to interact with the GUI at all.&lt;/p&gt;
&lt;p&gt;This is really great. By not forcing us down a single route, Strapi gets the best of both worlds.&lt;/p&gt;
&lt;h3&gt;Extensible Code &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#extensible-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Calling to the strengths of a robust language that&apos;s well known, developers can modify Strapi to their hearts content, as long as they don&apos;t want to update the core. And if they do, they can also turn to authoring a plugin, and hook in to Strapi in a way that separates our concerns out and allows us to write code in isolation.&lt;/p&gt;
&lt;p&gt;As I cautioned above, this could lead to Strapi being mis-used, and result in bloated or insecure code. However, Strapi&apos;s major advantage in this is that it is an API first, and a content authoring platform second.&lt;/p&gt;
&lt;p&gt;So whatever plugin authors do, they can never arbitratily affect the frontend of the website. Even if some malicious — or simply bad - code enters Strapi, rendering it unuseable, that needn&apos;t affect the frontend of the site at all, especially if it&apos;s a static site.&lt;/p&gt;
&lt;h3&gt;Good For Newer Developers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#good-for-newer-developers&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ultimately, developers are going to be the ones selling this product. We&apos;re the ones who hear about this new stuff and set expectations to decision makers about whether or not to use it.&lt;/p&gt;
&lt;p&gt;So it has to be accessible to a broad base, including (in my view) newer developers.&lt;/p&gt;
&lt;p&gt;I particularly frame this for newer developers not just to widen the catchment of a product but because it gives me a window into some other areas not directly related to the code: the helpfulness of the documentation, and a sense of community surrounding the project.&lt;/p&gt;
&lt;p&gt;It seems like a lot of effort has been made by the Strapi team to get clearly written and easy-to-follow documentation into the places where it can be found:- not just on the website and GitHub repo, but on 3rd party sites, like Medium, too.&lt;/p&gt;
&lt;p&gt;However, I have had difficulty with the documentation because it often refers to older versions of Strapi which are no longer maintained. I found this to be a bit of a minefield when I tried to initially use the Docker image that&apos;s available, and found the interface took me to the now-depracated Strapi Studio to create my APIs.&lt;/p&gt;
&lt;p&gt;I then deployed to Heroku using the Strapi 1-click deploy, and spent literally days trying to either modify the API or clone the site locally. Admittedly, this was less to do with Strapi than complications with using Heroku. However, it was a roadblock that delayed my using Strapi in production until I can fix that.&lt;/p&gt;
&lt;p&gt;To be fair, the community is very responsive and after joining the Slack channel I got answers to my questions very quickly.&lt;/p&gt;
&lt;h2&gt;Strapi - A Project I hope will grow &lt;a href=&quot;https://deliciousreverie.co.uk/posts/strapi-content-author-friendly-api/#strapi-a-project-i-hope-will-grow&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Overall, I really believe the Strapi authors have done it right. It meets the needs of content authors and developers really well. Although the docs leave a little to be desired, the project itself is making good progress.&lt;/p&gt;
&lt;p&gt;Personally, I can&apos;t wait to see what the future of Strapi means for content creation on the web.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Content-Author friendly APIs with Strapi&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Switching to BEM Syntax for CSS</title><link>https://deliciousreverie.co.uk/posts/switching-to-bem-syntax-for-css/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/switching-to-bem-syntax-for-css/</guid><description>Like a lot of things in the web world, CSS is a bit of mess. As the web gets more complicated, the tools and languages we use need to develop with us. This is currently happening as the W3C push forward with new functionality like Grid and Flexbox, but until those come into general use, we can aim to limit the risk of our code becoming unfathomable by other developers. </description><pubDate>Fri, 21 Oct 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Like a lot of things in the web world, CSS is a bit of mess. As the web gets more complicated, the tools and languages we use need to develop with us. This is currently happening as the W3C push forward with new functionality like Grid and Flexbox, but until those come into general use, we can aim to limit the risk of our code becoming unfathomable by other developers.&lt;/p&gt;
&lt;p&gt;I was a little suspicious of using BEM syntax for describing my CSS, I guess I didn&apos;t see the need for it and didn&apos;t have to work with other developers that much. But once I saw how it was being used on a project, I must say I was beginning to warm to the idea.&lt;/p&gt;
&lt;p&gt;If you take a look at the code on the &lt;a href=&quot;https://futurelearn.com/&quot;&gt;FutureLearn website&lt;/a&gt;, I find I can much more easily scan the code and identify the different elements that appear -- and more importantly, I can clearly follow the hierarchy of elements that are used on the site. Now, this hierarchy is critically important and something that can be very valuable to your projects.&lt;/p&gt;
&lt;h3&gt;Wait, doesn&apos;t SASS nesting give us hierarchy? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/switching-to-bem-syntax-for-css/#wait-doesn&apos;t-sass-nesting-give-us-hierarchy&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Yes, it&apos;s true that you can nest elements inside one another, and therefore more easily scan your CSS. So an element would be described something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.panel {
    ...

    .body {

        .title {
        ...
        }
    }
    .image {

        .caption {
        ...
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But have you ever looked at the output for that in your CSS file?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.panel {
    ...
}
.panel .body {
    ...
}
.panel .body . title {
    ...
}
.panel .image {
    ...
}
.panel .image .caption {
    ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although this is still fairly easy for us to scan quickly, the downside is that all of these extra selectors in your entire project up can eventually slow down the parsing of your css file.&lt;/p&gt;
&lt;p&gt;BEM allows us to use a much flatter format, which can be even more descriptive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.panel {}
.panel__body {}
.panel__title {}
.panel__image {}
.panel__caption {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These double underscores indicate that the selector is referring to an Element (the “E” in “BEM”) of the Block .panel. This keeps the CSS flatter and allows us to scan what the element is doing quickly.&lt;/p&gt;
&lt;p&gt;This gets even more useful when you add in Modifiers.&lt;/p&gt;
&lt;h2&gt;Modifiers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/switching-to-bem-syntax-for-css/#modifiers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An elements might have a variant that we would use in a different context:- say, for example, a navbar in the footer: we could re-use the same Block-level code, with a few modifications. Keeping with our example, we could do this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.panel—-large {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the HTML I would then have&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;article class=“panel panel—large”&amp;gt;
	…
&amp;lt;/article&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In other words, the Modifier class would only contain the modifier code.&lt;/p&gt;
&lt;p&gt;This is helping me to think a bit more about the structure of my code: I find that I’m looking for ways of making a class as succinctly as possible, because at some point it could be extended or re-used in some other way.&lt;/p&gt;
&lt;p&gt;For that reason, I don’t typically specify things like column widths inside Blocks, my sizes are a separate entity which contain these elements. That way I can specify how wide an item is by using a column, Flexbox, or (in the near future!) Grid syntax.&lt;/p&gt;
&lt;h2&gt;To Conclude &lt;a href=&quot;https://deliciousreverie.co.uk/posts/switching-to-bem-syntax-for-css/#to-conclude&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using BEM syntax makes logical sense, it’s not a huge thing to learn, and helps me keep my code clean and easy to follow by other developers. It doesn’t require any extra build tools but has increased my productivity quite a bit.&lt;/p&gt;
&lt;p&gt;More info: &lt;a href=&quot;https://getbem.com/introduction/&quot;&gt;getbem.com&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Switching to BEM Syntax for CSS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Tailwind progressively collapsing menu.</title><link>https://deliciousreverie.co.uk/posts/tailwind-ui-progressively-collapsing-menu/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/tailwind-ui-progressively-collapsing-menu/</guid><description>When I first saw this method of being able to progressively hide individual navigation items instead of all of them, and to do so without the use of JavaScript, I was impressed with the idea. Here&apos;s a remix using Tailwind.</description><pubDate>Wed, 18 Oct 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I had the opportunity to rework one of my favourite codepens recently. When I first saw this method of being able to progressively hide individual navigation items instead of all of them, and to do so without the use of JavaScript, I was impressed with the idea. Here&apos;s a remix using Tailwind.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The idea is to use &lt;code&gt;flex&lt;/code&gt; to display the individual elements. At small breakpoints, the &lt;code&gt;flex-basis&lt;/code&gt; is &lt;code&gt;100%&lt;/code&gt;, meaning each item stretches to the full width of the container.&lt;/p&gt;
&lt;p&gt;At medium breakpoints the &lt;code&gt;flex-basis&lt;/code&gt; reverts to the initial value (&lt;code&gt;auto&lt;/code&gt;), meaning they bunch up as blocks, much like they would if you &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Floats&quot;&gt;floated&lt;/a&gt; them.&lt;/p&gt;
&lt;p&gt;If you then set a fixed &lt;code&gt;height&lt;/code&gt; and hide the &lt;code&gt;overflow&lt;/code&gt; content, you effectively hide the items that don&apos;t fit on the top row.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;overflow&lt;/code&gt; can then be toggled with a visually hidden checkbox and an accompanying &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element (this is known as the &lt;a href=&quot;https://css-tricks.com/the-checkbox-hack/&quot;&gt;&quot;checkbox hack&quot;&lt;/a&gt;) that provides the functionality of a button.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;dywEaBY&quot; data-user=&quot;endymion1818&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&amp;gt;
&amp;lt;span&amp;gt;See the Pen &amp;lt;a href=&quot;https://codepen.io/endymion1818/pen/dywEaBY&quot;&amp;gt;
Tailwind Progressively Collapsing Navigation CSS Only&amp;lt;/a&amp;gt; by Ben Read (&amp;lt;a href=&quot;https://codepen.io/endymion1818&quot;&amp;gt;@endymion1818&amp;lt;/a&amp;gt;)
on &amp;lt;a href=&quot;https://codepen.io&quot;&amp;gt;CodePen&amp;lt;/a&amp;gt;.&amp;lt;/span&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;There&apos;s a couple of caveats with this:&lt;/p&gt;
&lt;h3&gt;1. It&apos;s not accessible.&lt;/h3&gt;
&lt;p&gt;This is probably the primary reason for not using it. We have hidden the checkbox and the label, because for screen reader users, they&apos;re not useful. I sincerely hope the navigation list is still useful to people with visual impairments. I&apos;ve experimented with &lt;code&gt;focus-visible&lt;/code&gt; to ensure all elements in the list are visible, but it is still quite jarring.&lt;/p&gt;
&lt;h3&gt;2. You can&apos;t automatically hide the &quot;more&quot; button&lt;/h3&gt;
&lt;p&gt;Without manually showing and hiding the toggle for the other items in the list, it will always show regardless of whether there are more items in the list or they are all on the page.&lt;/p&gt;
&lt;h3&gt;3. It&apos;s not easy to style the &quot;dropdown&quot;&lt;/h3&gt;
&lt;p&gt;Since this is an overflow area, it&apos;s not even part of a pseudo element. So changing the background only in the overflow area is not possible. And since we cannot know the height the overflow is going to be, we cannot easily make a pseudo element to fit that area.&lt;/p&gt;
&lt;h2&gt;An interesting experiment&lt;/h2&gt;
&lt;p&gt;As I said before, probably don&apos;t use this in production. There&apos;s probably a very small edge case where it would be more useful than a nav that collapses at a certain breakpoint.&lt;/p&gt;
&lt;p&gt;All in all, I still love the combination of CSS hacks that lets us do this sort of thing. It&apos;s what makes CSS such fun.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Tailwind progressively collapsing menu.&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Accessibility and you</title><link>https://deliciousreverie.co.uk/posts/talk-accessibility-you/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/talk-accessibility-you/</guid><description>I was invited to give a talk at London dev meetup JS Roundabout on the subject of accessibility. I recently discovered the talk has been put online ... </description><pubDate>Tue, 26 Jan 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Tuesday, 26 January 2021&lt;/p&gt;
&lt;p&gt;Over a year ago I was invited to give a talk at London dev meetup JS Roundabout on the subject of accessibility. I recently discovered the talk has been put online ...&lt;/p&gt;
&lt;p&gt;I was honoured to present this talk at JS Roundabout, who specifically contacted me on the back of some of my articles about accessibility, especially in the React space.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XdiIOd6wgMg&amp;amp;feature=emb_imp_woyt&quot;&gt;Watch on YouTube&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don&apos;t know about you but I always look back on myself with a critical eye. I talk too fast. I stutter sometimes and lose the thread. But my aim was to motivate and generate enthusiasm, and I think I achieved that. I hope I achieved that.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Accessibility and you&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How Were Using WordPress as a Headless CMS</title><link>https://deliciousreverie.co.uk/posts/talk-how-were-using-wordpress-as-a-headless-cms/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/talk-how-were-using-wordpress-as-a-headless-cms/</guid><description>At a recent WordPress conference in London I gave this talk. It was a terrifying and yet validating experience! </description><pubDate>Tue, 24 Apr 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Tuesday, 24 April 2018&lt;/p&gt;
&lt;p&gt;I&apos;m not a stranger to public speaking. But speaking on a technical subject? To people who are smarter than me? Not something I relish. Yet, I felt I had something to offer the WordPress community, so I volunteered to speak at one of the UK&apos;s biggest WordPress meetups, WordCamp London.&lt;/p&gt;
&lt;p&gt;UPDATE: Here&apos;s the talk hosted on &lt;a href=&quot;http://wordpress.tv/&quot;&gt;Wordpress.tv&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ever since starting my web development career I&apos;ve wanted to be in the place where I could help other developers out with their development dilemmas. It&apos;s taken me a long time to get to this stage, and it&apos;s only because of some great help from the following people:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chris Geary&lt;/li&gt;
&lt;li&gt;David Hewitt&lt;/li&gt;
&lt;li&gt;Steve Woodall&lt;/li&gt;
&lt;li&gt;Louise Towler&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s thanks to them imparting their knowledge and expertise (and sometimes their time), as well as others, that I have anything that might be considered worthy of sharing.&lt;/p&gt;
&lt;h2&gt;The Talk &lt;a href=&quot;https://deliciousreverie.co.uk/posts/talk-how-were-using-wordpress-as-a-headless-cms/#the-talk&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;My talk covered these main points:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;What is a headless (or decoupled) CMS&lt;/li&gt;
&lt;li&gt;Why use WordPress as a headless CMS&lt;/li&gt;
&lt;li&gt;Tools and process of building a headless site&lt;/li&gt;
&lt;li&gt;A basic example and some gotchas we found&lt;/li&gt;
&lt;li&gt;The Future is (probably) headless&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href=&quot;https://2018.london.wordcamp.org/session/how-were-using-wordpress-as-a-headless-cms/&quot;&gt;You can find it on the conference website too.&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;The experience &lt;a href=&quot;https://deliciousreverie.co.uk/posts/talk-how-were-using-wordpress-as-a-headless-cms/#the-experience&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;... was terrifying, to say the least! I rattled through it all in less than 30 mins (I had been given 35 minutes), but there were some really great comments &amp;amp; questions from the audience that I found fascinating. I&apos;m currently planning a series of blog posts on some of the questions that were raised.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How Were Using WordPress as a Headless CMS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>What should I use to build my new project?</title><link>https://deliciousreverie.co.uk/posts/tech-decision-tree/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/tech-decision-tree/</guid><description>There are so many choices when it comes to frameworks, and that&apos;s teriffic! But it could be bad news for people who struggle to make decisions. They might feel paralyzed just by the amount of choice that&apos;s on offer. So I made this with the aim of helping.</description><pubDate>Tue, 20 Jul 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;There are so many choices when it comes to frameworks, and that&apos;s teriffic! But it could be bad news for people who struggle to make decisions. They might feel paralyzed just by the amount of choice that&apos;s on offer.&lt;/p&gt;
&lt;p&gt;With that in mind, I&apos;ve given some deep thought into the tools I&apos;ve looked at recently, and historically, and tried to imagine how I might build an application, both frontend and backend, if I was just starting out.&lt;/p&gt;
&lt;p&gt;I came up with this chart. It&apos;s not a complete list of technologies, and it&apos;s at least a little biased towards my favourites. But it&apos;s hopefully something that can help unblock that paralysis, at least giving you a place to start.&lt;/p&gt;
&lt;p&gt;Making decisions about technology is difficult. I hope that this resource eases your pain even if just a little!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Backend &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tech-decision-tree/#backend&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This isn&apos;t mutually exclusive, but you might be spinning up a blog using Markdown or a 3rd party service. In which case, let&apos;s just skip this part and go to the frontend.&lt;/p&gt;
&lt;p&gt;Also, a lot of the frontends I&apos;ve mention have some sort of functions API built in. But since the backend isn&apos;t the focus of those tools, I&apos;ve left them out for simplicity&apos;s sake.&lt;/p&gt;
&lt;p&gt;If you&apos;re thinking you aren&apos;t going to need a lot of backend, likely the best thing to use would be lambdas. As I mentioned above, a lot of the frontend tools we have access to do have some sort of API for backend services. You could use that, or Netlify / Vercel&apos;s implementation, unless you&apos;re going directly to a cloud provider for your hosting.&lt;/p&gt;
&lt;p&gt;If you need more than this, you might want to consider whether you want to go the serverless route, or use a monolilth.&lt;/p&gt;
&lt;p&gt;Serverless tooling is a little less mature but it&apos;s probably more cost effective. You&apos;re not constantly paying for containers running in whatever service you&apos;re hosting on. So if you&apos;re needing a lot of backend it might pay dividends later.&lt;/p&gt;
&lt;p&gt;As I mentioned, the choices are very broad with monoliths. From straight-up CMS forms like Keystone, Ghost, Strapi, Payload and Apostrophe to full-blown frameworks such as Express, Meteor, Nest or Blitz.&lt;/p&gt;
&lt;p&gt;If you&apos;re looking at serverless, choices are decidedly more finite. There&apos;s a caveat though: some of the monoliths can run as serverless. Nest and Express are the most notable of these.&lt;/p&gt;
&lt;p&gt;Otherwise if you want a framework to build on, there&apos;s Redwood (which works particularly well on Netlify and soon Vercel) or Webiny (for AWS, Azure and others).&lt;/p&gt;
&lt;h2&gt;Frontend &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tech-decision-tree/#frontend&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The only JavaScript tool that doesn&apos;t have dynamic client-side routing out of the box is Eleventy. Eleventy&apos;s focus is on compiling away JavaScript, so you get a fast, browser-native experience out of the box.&lt;/p&gt;
&lt;p&gt;If you&apos;re focus is performance, and you need client-side routing, check out SvelteKit. Rather than shipping your framework to the frontend, SvelteKit compiles to pure JavaScript. But it still hydrates all of what you can see on your screen, meaning it&apos;s not as performant as Astro can be.&lt;/p&gt;
&lt;p&gt;Astro, a newcomer on the scene, does client-side routing via it&apos;s Collections API, other than that it&apos;s HTML all the way baby. I&apos;m particularly fond of Astro because you can use whatever frontend framework you want.&lt;/p&gt;
&lt;p&gt;If you&apos;re planning for a lot of client side JavaScript, dynamic routing and all, then your choices are on to whether you want a Vue-based or React-based framework.&lt;/p&gt;
&lt;p&gt;In the Vue world, there are plenty of choices, from the more low-level Nuxt, to Gridsome, which has a plugin ecosystem and build-time GraphQL API. It&apos;ll likely be quicker to use Gridsome because of this, but you may not need it.&lt;/p&gt;
&lt;p&gt;Similarly, Gatsby or NextJS are React frameworks. Gatsby has a large plugin ecosystem and GraphQL build-time API, whereas NextJS has a more low-level approach, you source data yourself, and plugins are scarce and likely not officially supported.&lt;/p&gt;
&lt;p&gt;I must admit i really enjoy using the build-time data layer approach. It&apos;s much easier to see when things are going to be rendered, and you can save API calls for your visitors by doing them upfront at the build stage.&lt;/p&gt;
&lt;p&gt;But also Next and Nuxt have some tricks up their sleeves. Next&apos;s innovated ISR &quot;incremental static regeneration&quot; means it can keep content more fresh than Gatsby currently can. However I imagine that&apos;s going to be a short-lived triumph.&lt;/p&gt;
&lt;h2&gt;Go forth and build! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tech-decision-tree/#go-forth-and-build!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Phew! There&apos;s a lot to choose from isn&apos;t there?!&lt;/p&gt;
&lt;p&gt;If you&apos;re still stuck at this point, and don&apos;t think you have a clear idea other than you want to build ... just choose one at random!! You will always learn something significant, and it&apos;ll be fun!&lt;/p&gt;
&lt;p&gt;It&apos;s never been more fun to build with JavaScript, the choices we have these days are staggering. I hope this has helped unblock your decision paralysis!!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:What should I use to build my new project?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Testing with Apollo Client mock provider</title><link>https://deliciousreverie.co.uk/posts/testing-apollo-client-mock-provider/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/testing-apollo-client-mock-provider/</guid><description>Apollo&apos;s MockProvider is a great tool for testing mutations, however it&apos;s a little bit magical, making errors a little difficult to find. If your testing your error state, this might come in handy. </description><pubDate>Sun, 02 Jan 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Sunday, 2 January 2022&lt;/p&gt;
&lt;p&gt;Apollo&apos;s MockProvider is a great tool for testing mutations, however it&apos;s a little bit magical, making errors a little difficult to find. If your testing your error state, this might come in handy.&lt;/p&gt;
&lt;p&gt;I&apos;m currently building a UI for a messages app, but encountered issues when testing sending new messages. Here&apos;s my component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export function SubmitForm() {
    const [message, setMessage] = useState(&apos;&apos;);
    const [submitMessage, { loading, error }] = useMutation(MESSAGE_MUTATION);

    return (
        &amp;lt;form
            onSubmit={event =&amp;gt; {
                event.preventDefault();

                try {
                    submitMessage({
                        variables: {
                            SendMessageInput: {
                                body: message,
                            },
                        },
                    });
                    setMessage(&apos;&apos;);
                } catch {
                    console.log(error);
                }
            }}
        &amp;gt;
            {error &amp;amp;&amp;amp; (
                &amp;lt;div&amp;gt;Sorry, there was a problem submitting your message&amp;lt;/div&amp;gt;
            )}
            &amp;lt;fieldset&amp;gt;
                &amp;lt;label htmlFor=&quot;input&quot;&amp;gt;Compose message&amp;lt;/label&amp;gt;
                &amp;lt;input
                    type=&quot;text&quot;
                    id=&quot;input&quot;
                    value={message}
                    onChange={event =&amp;gt; setMessage(event.target.value)}
                /&amp;gt;
            &amp;lt;/fieldset&amp;gt;
            &amp;lt;button type=&quot;submit&quot;&amp;gt;Send message {loading &amp;amp;&amp;amp; &amp;lt;Spinner /&amp;gt;}&amp;lt;/button&amp;gt;
        &amp;lt;/form&amp;gt;
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wrote a test suite for this component, all of which worked correctly, until I got to the stage when I was testing the error state:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;should render the error state UI&apos;, async () =&amp;gt; {
        const mockErrorMutation = {
            request: {
                query: MESSAGE_MUTATION,
                variables: {
                    SendMessageInput: {
                        body: &apos;test&apos;,
                    },
                },
            },
            error: new Error(&apos;drat&apos;),
        };

        render(
            &amp;lt;ThemeProvider theme={defaultTheme}&amp;gt;
                &amp;lt;MockedProvider mocks={[mockErrorMutation as any]}&amp;gt;
                    &amp;lt;SubmitForm /&amp;gt;
                &amp;lt;/MockedProvider&amp;gt;
            &amp;lt;/ThemeProvider&amp;gt;
        );

        const inputField = screen.getByLabelText(/compose message/i);
        const button = screen.getByText(&apos;Send message&apos;);

        userEvent.type(&apos;test&apos;);
        fireEvent.click(button);

        await waitFor(() =&amp;gt; {
            expect(
                screen.getByText(
                    /sorry, there was a problem submitting your message/i
                )
            ).toBeInTheDocument();
        });
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This test consistently failed, because all we ever got was the loading state. Yet manual testing passed fine.&lt;/p&gt;
&lt;p&gt;The solution? Async the submitMessage() function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;            onSubmit={async event =&amp;gt; {
                event.preventDefault();

                try {
                    await submitMessage({
                        variables: {
                            SendMessageInput: {
                                body: message,
                            },
                        },
                    });
                    setMessage(&apos;&apos;);
                } catch {
                    console.log(error);
                }
            }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without making this asynchronous it would always fail. Ah well. All&apos;s well that ends well.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Testing with Apollo Client mock provider&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Testing Packages Locally Using Lerna &amp; Verdaccio</title><link>https://deliciousreverie.co.uk/posts/testing-packages-locally-with-verdaccio-lerna/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/testing-packages-locally-with-verdaccio-lerna/</guid><description>Originally published on the Webiny blog. We want to provide the easiest way for developers to get started when building a new Webiny project. So we have a create-webiny-project command line interface (CLI) that creates a new user project with their chosen AWS region and some other preferences they need.</description><pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Tuesday, 1 November 2022 , originally at &lt;a href=&quot;https://www.webiny.com/blog/testing-packages-locally-with-verdaccio-lerna/&quot;&gt;https://www.webiny.com/blog/testing-packages-locally-with-verdaccio-lerna/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We want to provide the easiest way for developers to get started when building a new Webiny project. So we have a create-webiny-project command line interface (CLI) that creates a new user project with their chosen AWS region and some other preferences they need.&lt;/p&gt;
&lt;p&gt;This CLI requests over 100 packages which are published to NPM. These all work together to create GraphQL APIs, the Admin interface, and infrastructure-as-code that allow our four interrelated applications to work together.&lt;/p&gt;
&lt;p&gt;Modifying these packages and testing them could prove to be a challenge if it wasn&apos;t for very useful tools like &lt;a href=&quot;https://lerna.js.org/&quot;&gt;Lerna&lt;/a&gt;, which we use to publish new versions to NPM, and &lt;a href=&quot;https://verdaccio.org/&quot;&gt;Verdaccio&lt;/a&gt;. Verdaccio is a proxy registry. That means when configured it intercepts requests to NPM, and serves packages you&apos;ve published to it locally.&lt;/p&gt;
&lt;p&gt;Here&apos;s how we use leverage Verdaccio to test packages at Webiny, using &lt;a href=&quot;https://github.com/webiny/webiny-js&quot;&gt;our repository&lt;/a&gt; as an example.&lt;/p&gt;
&lt;h2&gt;Start Verdaccio&lt;/h2&gt;
&lt;p&gt;First of all, edit something in a package and build it. Then start Verdaccio. We can do this from the webiny-js project root directory using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn verdaccio -c .verdaccio.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The flag -c tells it to accept the config file we already have set up in the project.&lt;/p&gt;
&lt;p&gt;Next we need to set the registry address to use the port Verdaccio is running on instead of NPM&apos;s (&lt;a href=&quot;https://registry.npmjs.org/&quot;&gt;https://registry.npmjs.org&lt;/a&gt;) By default this is port 4873. Run this command to change the registry address:&lt;/p&gt;
&lt;p&gt;npm config set registry http://localhost:4873&lt;/p&gt;
&lt;p&gt;Did it work? To check, you can cat ~/.npmrc, you will hopefully see the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;registry=http://localhost:4873
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tricks With Yarn&lt;/h3&gt;
&lt;p&gt;If you&apos;re running Yarn v2 or greater, Yarn no longer recognizes this config file. Instead you will also need to create a .yarnrc.yml in the user root folder with the following parameters:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npmRegistryServer: &quot;http://localhost:4873&quot;unsafeHttpWhitelist: - localhost
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There&apos;s more information about this &lt;a href=&quot;https://verdaccio.org/docs/setup-yarn/#yarn-berry-2x&quot;&gt;on Verdaccio&apos;s docs site&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Version and Release Packages&lt;/h2&gt;
&lt;p&gt;The final steps before we test our user project is to create a new Beta version and release the packages. This is where our existing setup in the Webiny project with Lerna steps in. You can see details of how this command works by &lt;a href=&quot;https://github.com/webiny/webiny-js/blob/next/package.json#L157&quot;&gt;looking at the package.json in our repository&lt;/a&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn lerna:version:verdaccio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Under the hood this runs the Lerna version command but doesn&apos;t push or create a changelog. It also creates a new tag for the release. If this is the first time you&apos;ve run the command, the tag will be v{version}-beta.0, then v{version}-beta.1, and so on. Once this has completed you will see a new release commit on the branch which you will need to roll back afterwards. Now we can release the new version to Verdaccio.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn lerna:publish:verdaccio
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This takes the existing packages with the appropriate tag and publishes them to our local Verdaccio registry. Please note that if you are running Verdaccio on anything other than the default port you&apos;ll need to run the full command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yarn lerna publish from-package --dist-tag next --registry=\&quot;http://localhost:{port}\&quot; --no-verify-access --no-verify-registry --yes
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Create a New User Project&lt;/h2&gt;
&lt;p&gt;The next step is to create a new Webiny user project. You can create it in a subdirectory of the webiny-js repo, or in another folder. We can use NPX to do this using the following command:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npx create-webiny-project@next test-project --tag next --no-interactive &quot;unsafeHttpWhitelist&quot;:[&quot;localhost&quot;]}&apos; --template-options &apos;{&quot;region&quot;:&quot;us-east-1&quot;,&quot;storageOperations&quot;:&quot;ddb&quot;}&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can of course replace test-project with something more relevant, and set it to deploy to a different AWS region other than us-east-1 if you wish.&lt;/p&gt;
&lt;p&gt;Once this has finished you should be able to run yarn webiny --version and see your newly published beta tag.&lt;/p&gt;
&lt;p&gt;You can now deploy your project as normal and test any changes you made.&lt;/p&gt;
&lt;h2&gt;Tear Down and Roll Back&lt;/h2&gt;
&lt;p&gt;Once you have some changes you need to make, or you&apos;re satisfied that your changes won&apos;t break anything, you need to roll back your system and the webiny-js repo to it&apos;s previous state.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop Verdaccio&lt;/li&gt;
&lt;li&gt;Point local registry back to npmjs.org:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;`npm config set registry https://registry.npmjs.org``&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Remove yarn config files&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;`rm ~/.yarnrc.yml# if you think you might need this file later, you can instead do mv ~/.yarnrc.yml ~/.yarnrc.yml.bkup``&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Roll back the release commit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;git reset --hard HEAD~&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delete the prerelease tag&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;git tag -d${tag}&lt;/p&gt;
&lt;h2&gt;Verdaccio: A Great Solution to a Common Problem&lt;/h2&gt;
&lt;p&gt;As you can see, Verdaccio is very easy to use, and in fact we have hardly spent any time configuring Verdaccio since it has been set up. For the most part, we&apos;ve used the &lt;a href=&quot;https://github.com/webiny/webiny-js/blob/next/.verdaccio.yaml&quot;&gt;default configuration&lt;/a&gt;. Leveraging Lerna&apos;s ability to publish packages quickly and easy has meant that we can concentrate on delivering great software instead of spending more time on our tooling.&lt;/p&gt;
&lt;p&gt;If it helps, we also created a video of this process:&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Testing Packages Locally Using Lerna &amp;amp; Verdaccio&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Testing Web Components</title><link>https://deliciousreverie.co.uk/posts/testing-web-components/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/testing-web-components/</guid><description>I&apos;ve been taking a deep dive into Web Components recently: building a complex application. I have really enjoyed it. However, as with any project I want to deliver something that is proven to work by means of static, unit and end-to-end tests. I&apos;ve run in to some issues with existing test suites.</description><pubDate>Tue, 23 Apr 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I&apos;ve been taking a deep dive into Web Components recently: building a complex application. I have really enjoyed it. However, as with any project I want to deliver something that is proven to work by means of static, unit and end-to-end tests. I&apos;ve run in to some issues with existing test suites.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When I deliver a new application I want it to be as well documented as possible so that any changes to it can be made as easily as possible, and by people who don&apos;t know the codebase as well as I would.&lt;/p&gt;
&lt;p&gt;To that end, making sure that what I hand over is reasonably tested is paramount to my own sense of professionalism. It&apos;s only this that gives me a strong sense of accomplishment because I know it stands a good chance of lasting a long time, and it&apos;ll be easy for other people with less prior knowledge to work with.&lt;/p&gt;
&lt;p&gt;When I deliver code based on Web Components, I want to deliver on that same level of quality. I know I&apos;m leaning on newer browser-native technologies, but they&apos;re not that new.&lt;/p&gt;
&lt;p&gt;I was therefore very surprised to find no adequate way to provide unit tests for Web Components.&lt;/p&gt;
&lt;h2&gt;My Testing Trophy&lt;/h2&gt;
&lt;p&gt;I&apos;ve decided to adopt a standardised set of tools across all of our codebases so that non-specialist developers can drop into any of them with the least amount of friction that include the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Static analysis via JSDoc&lt;/li&gt;
&lt;li&gt;Unit testing via Vitest&lt;/li&gt;
&lt;li&gt;E2E testing via Cypress&lt;/li&gt;
&lt;li&gt;Manual testing with Storybook&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most of these tools work really well, but one definitely doesn&apos;t...&lt;/p&gt;
&lt;h2&gt;Abstractions, abstractions everywhere&lt;/h2&gt;
&lt;p&gt;I think one of the problems is that for a large proportion of people adopting Web Components, they&apos;re here after having had experience with another framework, and looking for something equivalent.&lt;/p&gt;
&lt;p&gt;That&apos;s definitely not true in my case. I was a little annoyed at the way React forces you to think about JavaScript, and I wanted to work more closely with the browser instead of seeming to force JavaScript into a certain pattern.&lt;/p&gt;
&lt;p&gt;Fundamentally that meant that I wanted to remove the abstractions of a framework.&lt;/p&gt;
&lt;p&gt;There have been well meaning attempts to make a framework out of Web Components. And I see why: If you&apos;re used to a framework, which a lot of people will be saying &quot;here&apos;s another framework, but for Web Components&quot;.&lt;/p&gt;
&lt;p&gt;The good thing about those frameworks is that they try to make something look a little more familiar, even when it&apos;s not. I looked at Lit Element extensively, and it definitely feels more like Vue. And they&apos;ve got their ecosystem of tools and testing libraries and tutorials, which certainly helps adoption.&lt;/p&gt;
&lt;p&gt;Again, though, that&apos;s not what I&apos;m after.&lt;/p&gt;
&lt;p&gt;I am a little freaked out going back to classes instead of purely functional components like in React. However there are benefits; understanding inheritance much clearer when you&apos;re writing something like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyComponent extends HTMLElement {}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are other pros and cons, but I want to focus on the subject at hand.&lt;/p&gt;
&lt;h2&gt;Cypress: A Hidden Option&lt;/h2&gt;
&lt;p&gt;Ok well, not really hidden, but I did not realise I had to manually turn &lt;em&gt;on&lt;/em&gt; a setting to enable Cypress to recognise the shadow DOM:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// in cypress.config.js

const config = defineConfig({
  // ...other config
  e2e: {
    includeShadowDom: true,
  },
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I can write tests that include shadow dom selectors:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;describe(&quot;template spec&quot;, () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    cy.visit(&quot;http://localhost:5173&quot;);
  });
  it(&quot;should play&quot;, () =&amp;gt; {
    cy.get(&quot;media-player#my-player&quot;)
      .shadow()
      .find(&apos;media-control-bar&apos;)
      .find(&quot;media-play-button&quot;).
      click();
    cy.get(&quot;media-player#my-player&quot;)
      .should(&quot;have.attr&quot;, &quot;mediaplaying&quot;, &quot;true&quot;);
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great! Annoying if you don&apos;t know about it, but works fine once you do.&lt;/p&gt;
&lt;h2&gt;Storybook: Works fine&lt;/h2&gt;
&lt;p&gt;I&apos;ve found Storybook particularly useful for manually testing that each argument I pass to the component works properly. And since Storybook re-renders the component when you change an input, you can see the player reacting to the arguments you pass it.&lt;/p&gt;
&lt;p&gt;It is quite verbose. I have to pass my arguments around several times, but it does a good job once you&apos;ve got those in:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
// basic setup
export default {
  title: &apos;Media Embeds&apos;,
  // these allow controls to render for the arguments
  // here&apos;s the first time I&apos;m declaring them
  argTypes: {
    videourl: { 
      control: &apos;text&apos;,
    },
    posterurl: {
      control: &apos;text&apos;,
    },
  }
};

// I found arguments needed to be destructured and
// passed explicitly into my component
// That&apos;s 2 and 3
const Template = ({
  videourl,
  posterurl
}) =&amp;gt; `
  &amp;lt;media-player 
    videourl=&quot;${videourl}&quot;
    posterurl=&quot;${posterurl}&quot;
  &amp;gt;&amp;lt;/media-player&amp;gt;
`;

export const Player = Template.bind({});


// Now actually declaring defaults for those arguments, 5th time

/**
 * @type {UserOptions}
 */
Player.args = {
  videourl: &apos;https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/high.mp4&apos;,
  posterurl: &apos;https://image.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/thumbnail.jpg&apos;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There could be a way to reduce this, hopefully I&apos;ll be able to investigate before I hand the project over.&lt;/p&gt;
&lt;h2&gt;Vitest .. you&apos;re on your own&lt;/h2&gt;
&lt;p&gt;I shouldn&apos;t really say this is a Vitest problem. It would be the same if you were using Jest or anything else.&lt;/p&gt;
&lt;p&gt;The problem really lies with DOM mocking libraries. None of them allow you to extend the HTML Element.&lt;/p&gt;
&lt;p&gt;I have found a library of helpers that got me way further than I had with anything else.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ficusjs.org/&quot;&gt;Ficus&lt;/a&gt; is billed as a lightweight abstraction of Web Components. There&apos;s that word again. But they do have a range of other tools that have been helpful: paticularly a wrapper around JSDom that includes favourable setup for Web Components.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { describe, it, beforeEach, expect } from &apos;vitest&apos;;
import { init } from &apos;@ficusjs/testing&apos;

import &apos;../src/app.js&apos;;

describe(&quot;&amp;lt;media-player&amp;gt; &quot;, () =&amp;gt; {
  beforeEach(() =&amp;gt; {
    init();
  });
  it(&quot;should render&quot;, async () =&amp;gt; {
    document.body.innerHTML = &apos;&amp;lt;media-player&amp;gt;&amp;lt;/media-player&amp;gt;&apos;;
    expect(document.querySelector(&apos;media-player&apos;)).to.exist;
  });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This still meant I have to mock a whole bunch of stuff to even get it to render. And this is as far as I have got. Not even the venerable, cross-framework testing-library &lt;a href=&quot;https://testing-library.com/docs/dom-testing-library/intro/&quot;&gt;covers the Shadow Dom&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I do have a lot of stuff I can unit test with Vitest. But there&apos;s so much more I want to do with it.&lt;/p&gt;
&lt;p&gt;If you know anything that might help, please let me know.&lt;/p&gt;
&lt;p&gt;I&apos;ve also created a GitHub repo with examples if you&apos;re coming across this and are looking for resources for testing Web Components:&lt;/p&gt;
&lt;p&gt;https://github.com/endymion1818/example-web-component-test-suite&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;More than anything, it&apos;s been a liberating, fun experience to see how far the web has come in the last few years. I hope I get to work with Web Components for a long time to come.&lt;/p&gt;
&lt;p&gt;And I hope more developers can start using them too.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Testing Web Components&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The Future is Headless</title><link>https://deliciousreverie.co.uk/posts/the-future-is-headless/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/the-future-is-headless/</guid><description>If you&apos;ve been in the industry for any length of time, the chances are that you&apos;ve been in one of those discussions about CMSes that are never resolved. That is about to change forever ... </description><pubDate>Mon, 10 Aug 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Monday, 10 August 2015&lt;/p&gt;
&lt;p&gt;If you&apos;ve been in the industry for any length of time, the chances are that you&apos;ve been in one of those discussions about CMSes that are never resolved. That is about to change forever ...&lt;/p&gt;
&lt;p&gt;Content management systems have revolutionised web development. From the start of their existence, we haven&apos;t had web sites any more—we&apos;ve had web systems.&lt;/p&gt;
&lt;p&gt;It&apos;s the web&apos;s innovation which has contributed to the success of the medium that it is today.&lt;/p&gt;
&lt;p&gt;But it does mean that we&apos;re always on a bit of a back foot, not understanding all of what is going on around us—because it&apos;s in a constant state of flux.&lt;/p&gt;
&lt;p&gt;Today I saw a video from Drupalcon that I immediately thought: &quot;That&apos;s the future.&quot;&lt;/p&gt;
&lt;h3&gt;What is headless? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/the-future-is-headless/#what-is-headless&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A headless CMS is just that - headless. It doesn&apos;t have a web front end. So there is no templating system to hack around. The CMS doesn&apos;t affect the web front end in the same way it does today.&lt;/p&gt;
&lt;p&gt;It still runs the website, but in a drastically different way, using JSON data and REST.&lt;/p&gt;
&lt;p&gt;Think about this: no more PHP mixed in with your HTML. No more overriding plugins that inject icons into your &amp;lt;head&amp;gt; that you&apos;re never going to use. Ever.&lt;/p&gt;
&lt;p&gt;Sounds like a massive step up to me.&lt;/p&gt;
&lt;p&gt;But there are many other advantages to serverside developers and organisations alike. Users can publish their content once and have it displayed on the app, the website and on syndicated sites all at the same time. CMSes can be updated without breaking the frontend ... because there is no frontend.&lt;/p&gt;
&lt;h3&gt;What it will ask of us &lt;a href=&quot;https://deliciousreverie.co.uk/posts/the-future-is-headless/#what-it-will-ask-of-us&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With any technology change, we have to learn new skillsets. It takes time. But with such clear advantages, and the two biggest CMSes already developing headless systems (&lt;a href=&quot;https://groups.drupal.org/headless-drupal&quot;&gt;Drupal&lt;/a&gt; and &lt;a href=&quot;https://v2.wp-api.org/&quot;&gt;Wordpress&lt;/a&gt;) it might be time to start that road.&lt;/p&gt;
&lt;p&gt;Instead of frontend developers having to also know PHP, we will need to concentrate on how to use JSON data via REST. With a frontend framework such as Angular or Backbone, this becomes ... easier.&lt;/p&gt;
&lt;p&gt;Actually, I&apos;m having trouble writing this section here with confidence ... I&apos;m still working out how it all comes together. But I don&apos;t think that&apos;s a bad thing.&lt;/p&gt;
&lt;p&gt;Sit through this video, if it blows your mind like it did mine, then we have an interesting road to travel ahead.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=GX9z5M9mz30&quot;&gt;Full video&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The Future is Headless&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The Art of Compromise</title><link>https://deliciousreverie.co.uk/posts/the-art-of-compromise/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/the-art-of-compromise/</guid><description>Working as part of a larger team often involves skills that aren&apos;t readily taught as part of a person&apos;s development career. But they are essential if you&apos;re going to be developing software as part of a team. </description><pubDate>Thu, 08 Aug 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;We developers are inherently precious about things. Our tools. Our technologies. But we can&apos;t afford to be precious about those things too much. In fact, working in cross-functional teams and with other teams means that we sometimes need to compromise.&lt;/p&gt;
&lt;p&gt;I was seething.&lt;/p&gt;
&lt;p&gt;So upset that, I have to admit, I came close to criticising someone whose opinions I really value, and who is a more skilled developer than I. This was because they said they didn&apos;t like the way I was leveraging the CSS cascade: &quot;It makes components leaky and not well encapsulated&quot;.&lt;/p&gt;
&lt;p&gt;I love the cascade, and have been using it to pass down colours to components instead of re-declaring colours on each paragraph, heading and link inside nested components. I thought this was an approach that everyone should take: use the cascade to our advantage.&lt;/p&gt;
&lt;p&gt;I nearly voiced some critical opinions to my colleage. Then our designer spoke up. &quot;We need to stop discussing this between just two people. Involve all of the other developers working on this set of components. Then you can reach consensus and all be in alignment.&quot;&lt;/p&gt;
&lt;p&gt;He was right. Everyone has an opinion, and whether one is correct or another is isn&apos;t up for debate. What truly matters is what the consensus is.&lt;/p&gt;
&lt;p&gt;Compromise is a strength, not a weakness. And on any project that involves more than one person (which is, like, all of them), it&apos;s the only way of moving forwards.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The Art of Compromise&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Things I Learned By Working for Free</title><link>https://deliciousreverie.co.uk/posts/things-i-learned-working-for-free/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/things-i-learned-working-for-free/</guid><description>Developers sometimes get asked to do stuff for free. If that happens, it generally can be a warning sign that would be right to steer well clear of. It&apos;s easy to end up feeling cheated. But I&apos;ve been fortunate to be involved in two projects that, although I earned no money from, I felt that I benefited greatly.</description><pubDate>Sat, 14 Mar 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Developers sometimes get asked to do stuff for free. If that happens, it generally can be a warning sign that would be right to steer well clear of. It&apos;s easy to end up feeling cheated. But I&apos;ve been fortunate to be involved in two projects that, although I earned no money from, I felt that I benefited greatly.&lt;/p&gt;
&lt;h3&gt;Project 1: Edifice &lt;a href=&quot;https://deliciousreverie.co.uk/posts/things-i-learned-working-for-free/#project-1:-edifice&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Edifice came about from the need that a large voluntary organisation had for tracking teams, projects and skill levels on construction projects throughout London, Hertfordshire and Oxfordshire.&lt;/p&gt;
&lt;p&gt;The lead developer, a personal friend, invited me to join the project initially as a front end developer. When I started, my friend was training a largely unskilled workforce in a huge range of different abilities that he needed for his project, including HTML, CSS, Java, JavaScript, and others.&lt;/p&gt;
&lt;p&gt;I at once suggested that to save development time he could use a preexisting framework like Foundation or Bootstrap, which would allow the 3 or 4 skilled developers to work on the rest of the Java MVC web app, whilst the less skilled could work on refining the front end views.&lt;/p&gt;
&lt;p&gt;Working on this project gave me an opportunity to learn first hand from a former university lecturer about so many subjects. Among other things, I learned how internet protocols work, why we use relational databases instead of spreadsheets and loads of other stuff.&lt;/p&gt;
&lt;p&gt;I also got to work closely with another experienced programmer from whom I learned how to map processes and from these write user stories, how to use Git and Github, and even how to format my code properly.&lt;/p&gt;
&lt;p&gt;Yes, the project was for no monetary value, but was genuinely instrumental in my career.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/RBC1B/ROMS&quot;&gt;Edifice on Github&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Project 2: &lt;a href=&quot;http://freebabylon5.com/&quot;&gt;freebabylon5.com&lt;/a&gt; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/things-i-learned-working-for-free/#project-2:-freebabylon5.com&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I volunteered to build this website because - as you&apos;ll know if you follow me on Twitter - I love this 90s TV show. The fan campaign to get the show back on television is run by one stalwart fan, and they needed a home website.&lt;/p&gt;
&lt;p&gt;I built the site on Bootstrap and Wordpress, very quickly. I think it was in about 3 weeks in my spare time. Whilst it was fun, I didn&apos;t do anything spectacularly original during the build.&lt;/p&gt;
&lt;p&gt;But after the project went live, I tracked user flows and kept in touch with the fans via social media, and iterated on the site. It was great to have fan feedback and suggestions, and to see when I changed something on the site, what the results could be.&lt;/p&gt;
&lt;p&gt;Being motivated to refine the site after the go live taught me so much about how a website is a living document that can change to fit the changing needs of its users.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://freebabylon5.com/&quot;&gt;freebabylon5.com&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The Internet - Built on Free &lt;a href=&quot;https://deliciousreverie.co.uk/posts/things-i-learned-working-for-free/#the-internet-built-on-free&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A huge chunk of the progress in web standards, in the ability to do incredible stuff with websites, in what we know as the internet today came about by dedicated people who were willing to invest their time and effort building the community and constructing the things we benefit from each day.&lt;/p&gt;
&lt;p&gt;So-doing stuff for free, generally a bad idea. Unless there&apos;s some personal gain or it&apos;s for a cause you genuinely care about and are willing to invest in.&lt;/p&gt;
&lt;p&gt;You may be surprised what you can learn from getting involved in many incredible projects that really need your support.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Things I Learned By Working for Free&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>The WordPress &apos;Implosion&apos;</title><link>https://deliciousreverie.co.uk/posts/the-wordpress-implosion/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/the-wordpress-implosion/</guid><description>Many years ago I moved away from building things with WordPress. But it&apos;s time I tried to help people understand what I think the current situation means for people still in the ecosystem.</description><pubDate>Thu, 17 Oct 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;Many years ago I moved away from building things with WordPress. But it&apos;s time I tried to help people understand what I think the current situation means for people still in the ecosystem.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you haven&apos;t heard about it yet, try entering &quot;WordPress&quot; and &quot;WPEngine&quot; into a search engine.&lt;/p&gt;
&lt;p&gt;Some 8 or so years ago I was fairly invested in the WordPress ecosystem. I liked how empowering WordPress was as a foundation for people without a technical background to be able to create their own content-rich websites.&lt;/p&gt;
&lt;p&gt;However when they introduced the &quot;Gutenberg&quot; editor, I read the writing on the wall.&lt;/p&gt;
&lt;h2&gt;An editor that empowers ... who?&lt;/h2&gt;
&lt;p&gt;Gutenberg was a new editor environment that focused WordPress heavily on the end user. It allowed users to create more dynamic pages and to edit more content that would have been previously outside of their ability to change (headers and footer content, particularly).&lt;/p&gt;
&lt;p&gt;Their decision to merge Gutenberg into WordPress Core brought WordPress much closer to their user base. That&apos;s a decision I applaud: its great that they want to be the ones to empower non-technically minded people.&lt;/p&gt;
&lt;p&gt;For me, I realised it would be incredibly difficult to support what the editor allowed users to do. I couldn&apos;t standardise even a simple thing like a button, because the user could override it using Gutenberg. I couldn&apos;t standardise templates, because Gutenberg could inject layout blocks that didn&apos;t work on the template I had created.&lt;/p&gt;
&lt;p&gt;It was clear from this that WordPress as an organisation didn&apos;t care about the intermediaries: digital agencies and other technical organisations. WordPress didn&apos;t value us. It was instead built for the far broader, non technical user base. That was &lt;em&gt;all&lt;/em&gt; it was built for. I don&apos;t think we were even a consideration.&lt;/p&gt;
&lt;p&gt;At that point, I decided that I was out. I knew that my technical decisions would continue to be impacted negatively by WordPress as an organisation.&lt;/p&gt;
&lt;p&gt;I moved on to have a successful career with JavaScript and left the ecosystem behind.&lt;/p&gt;
&lt;p&gt;And hearing all that&apos;s been happening, if you&apos;re a developer or own a digital agency or other technical company, one that builds sites for end users with WordPress, I encourage you to do the same.&lt;/p&gt;
&lt;h2&gt;Digital agencies are not WordPress customers&lt;/h2&gt;
&lt;p&gt;Digital agencies and technical organisations that deliver sites built on WordPress are not who WordPress sees as it&apos;s customers.&lt;/p&gt;
&lt;p&gt;Therefore the WordPress organisation will continue to make decisions that could easily have a negative impact on your technical decisions for years to come. Impacts that will erode your reputation and your relationships with your users.&lt;/p&gt;
&lt;p&gt;Instead, I encourage you to build with some other tool that &lt;em&gt;is&lt;/em&gt; built for technical organisations, where we &lt;em&gt;are&lt;/em&gt; it&apos;s customer. And therefore &lt;em&gt;your&lt;/em&gt; customers will continue to see you as those who support them.&lt;/p&gt;
&lt;h2&gt;What should I use instead?&lt;/h2&gt;
&lt;p&gt;There are plenty of good alternatives. Ones that are small and ones that support massive content rich sites. I&apos;m not even going to mention any them by name here; they&apos;re one deft search string away. If you know me already, then you know which ones I recommend.&lt;/p&gt;
&lt;p&gt;If you&apos;re invested in the WordPress ecosystem, I encourage you to build commercial themes and plugins for WordPress: Support their ecosystem and let them support their users.&lt;/p&gt;
&lt;p&gt;If not, it&apos;s time to move on. The heyday is over. Let users be users. And let WordPress be WordPress.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:The WordPress &apos;Implosion&apos;&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Three Conversations to Serverless</title><link>https://deliciousreverie.co.uk/posts/three-conversations-to-serverless/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/three-conversations-to-serverless/</guid><description>When Im explaining the idea of Serverless architecture it often takes three conversations before someone understands where I&apos;m coming from, why we put up with the sometimes difficult to understand restrictions it puts on development, and ultimately why it can be extremely useful. In this post, I&apos;m going to document these three conversations that Ive had with many people in the last few months. </description><pubDate>Fri, 09 Nov 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Serverless isn&apos;t such a new concept but it is still difficult to understand for many of us. I&apos;ve been privileged to work with some great people over the past few months, and I&apos;ve really been helped to understand serverless because of the questions they&apos;ve raised, many of which I couldn&apos;t answer straight away. Often their questions have got me thinking about different ways to explain serverless.&lt;/p&gt;
&lt;p&gt;Since many of these people are used to managing, developing or supporting a project that&apos;s more monolithic in nature, I&apos;m going to try to write down some of those questions in the form of the paths they often take: realisation, clarification, and comprehension. If you find you don&apos;t understand the concept of serverless after these 3 &quot;conversations&quot;, remember that the idea of 3 conversations is perhaps a generalisation. One of my colleagues, a much more senior developer than I, was persuaded of the approach after several weeks of discussions!&lt;/p&gt;
&lt;h2&gt;1. Realisation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-conversations-to-serverless/#1.-realisation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Often, people in my organisation come to me with a requirement for the project, or a solution to an outstanding problem, with an idea of how they would implement it. This is because we are often predisposed to solutions-based thinking. There have been studies of the brain to show we are likely hard wired to do this. It helps us to act quickly based on our previous experiences.&lt;/p&gt;
&lt;p&gt;However, with serverless there&apos;s usually less common ground with previous experiences we might have had. Often, during the conversation this statement comes up:&lt;/p&gt;
&lt;p&gt;&quot;there is no backend&quot;&lt;/p&gt;
&lt;p&gt;When I reach this point in a conversation, it&apos;s the point that the listener realises the objective they currently have isn&apos;t possible in the way they have been thinking.&lt;/p&gt;
&lt;p&gt;Whether it&apos;s to dynamically render content, process form data, implement redirects, or modify content using a CMS, this statement stops them short. There is no back end to our static frontend that is, in our case, hosted on a dumb S3 bucket and uploaded to a dumb CDN. I mean &quot;dumb&quot; as in there is no processing ability there we can leverage.&lt;/p&gt;
&lt;p&gt;Usually, this realisation prompts some critical thinking. &quot;How can I solve my problem with this restriction?&quot; And that&apos;s when it starts to get a bit frustrating. Which means you might need some clarification.&lt;/p&gt;
&lt;h2&gt;2. Clarification &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-conversations-to-serverless/#2.-clarification&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You, the listener, have probably been mulling over your problem for a while now, and whilst you realise you can&apos;t stop a train that&apos;s in progress you might be thinking this whole serverless thing is a ludicrous idea.&lt;/p&gt;
&lt;p&gt;Why would anyone build apps this way? Why make it so complicated?! When you had a monolithic app, it could do anything you wanted out-of-the-box. Now you might be finding out things. Things that might have been unknown to you before. For instance, form data needs to be processed server-side before it&apos;s sent anywhere. That a lot of 3rd party services require auth tokens, which we can&apos;t use in JavaScript because it can be read and used by anyone.&lt;/p&gt;
&lt;p&gt;This can be frustrating. These paradigms are hard to come to terms with, at least initially. But with each restriction there is a solution that comes with some really interesting benefits. And that&apos;s when clarification can turn into comprehension.&lt;/p&gt;
&lt;h2&gt;3. Comprehension &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-conversations-to-serverless/#3.-comprehension&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Having gone through this process of realising we are dealing with something very different from what you might have imagined, to the clarification that you can&apos;t approach your requirement in the same way you have done previously, now we can start to solve the new problem.&lt;/p&gt;
&lt;p&gt;When we start doing that, we can find there are some really nice solutions that add a layer of curiosity and, you might say, delight, as listeners come to find out more about the options they have.&lt;/p&gt;
&lt;p&gt;Heres a few of the &quot;lightbulb&quot; moments I&apos;ve enjoyed witnessing over the course of the past few months, credited where I can remember who it was:&lt;/p&gt;
&lt;p&gt;&quot;Ah so there&apos;s more security and better performance, that&apos;s good then.&quot;&lt;/p&gt;
&lt;p&gt;&quot;So you can get that data from the API at build time, and update it if necessary on the frontend. That&apos;s useful.&quot; (Tim)&lt;/p&gt;
&lt;p&gt;&quot;[to other stakeholders] ... and then anyone else can consume this service if they need to, it&apos;s already built for them.&quot;&lt;/p&gt;
&lt;p&gt;&quot;I can see why you wanted to use Gatsby for this project. GraphQL is ... is really useful actually&quot; (Awais)&lt;/p&gt;
&lt;p&gt;&quot;So you can do that even before the site gets to the user? And it would be one request?&quot; (Jonny)&lt;/p&gt;
&lt;p&gt;&quot;[After we reported straight As in &lt;a href=&quot;http://webpagetest.org/&quot;&gt;webpagetest.org&lt;/a&gt;] Really impressive lads! Good work!&quot; (Tomas)&lt;/p&gt;
&lt;p&gt;This has been a lot of fun for me. I have particularly enjoyed helping others see things in a different light, or with renewed potential. It&apos;s great to see people get excited about something so much that they want to go away and do something with it.&lt;/p&gt;
&lt;p&gt;Serverless is still only one method to approach development. It&apos;s not always the best way, but it is a viable solution to certain situations and use cases. But it really does take time to come to terms with the idea. Teams you interface with also need to be aware that there are things that are going to be different and some problems may require a very different problem solving route.&lt;/p&gt;
&lt;p&gt;Static sites in particular shouldn&apos;t be made to do everything. Sometimes complexity on the frontend can be taken too far.But we have managed to build a site that is robust, fast, beautifully designed, deceptively simple, and that has some useful microservices that can also be utilised elsewhere.&lt;/p&gt;
&lt;p&gt;Working with serverless is definitely my thing. I&apos;m really looking forward seeing more implementation of this approach in the future.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Three Conversations to Serverless&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Three UI Component Conundrums</title><link>https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/</guid><description>I really like using React with Styled Components for building effective user interfaces. However I have recently come across three conundrums to solve. </description><pubDate>Fri, 30 Oct 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;React combined with Styled Components is a flexible and maintainable way of composing reusable user interfaces for the web. However, every set of tools has as well as awesome features, a few tricky problems to solve. Here are three that I&apos;ve come across recently.&lt;/p&gt;
&lt;h2&gt;Some conundrums &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#some-conundrums&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. CSS inheritance: The conundrum &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#1.-css-inheritance:-the-conundrum&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The first issue concerns encapsulation. Broadly speaking, the main differing opinions on this argue that the results of your functions should be bound to that function only, and not have any effect on anything outside that variable (this is encapsulation), versus the idea that it&apos;s OK for things to inherit, or to have an effect that carries on across multiple functions.&lt;/p&gt;
&lt;p&gt;I&apos;m weighing into this discussion on a different level to most. In this post, I&apos;m not talking about encapsulation and inheritance as it pertains to object-oriented programming, although the principles are the same. I&apos;m chiefly interested in UI components, and here I&apos;m going to talk about these principles and how they&apos;ve impacted my recent work.&lt;/p&gt;
&lt;p&gt;The basic tenet of CSS assumes inheritance. It&apos;s in the name: CascadingStyle Sheets. If you started your web development career from this angle, then you&apos;re probably quite used to seeing things like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;div {
  color: lightblue;
  background-color: green;
}
div.banner {
  color: green;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The div with the class banner will inherit the background-color from the first div. This is a normal way of writing CSS ... if you&apos;ve had a certain type of introduction to CSS.&lt;/p&gt;
&lt;p&gt;At work, I&apos;m using an internal library of components written in Styled Components. This has some really distinct advantages: it&apos;s very simple for developers to keep within the brand guidelines, and even very basic elements are encapsulated, that is, they can be composed together without any unexpected side effects.&lt;/p&gt;
&lt;p&gt;But this has also led us into some complications... here&apos;s the conundrum and solution for two of them:&lt;/p&gt;
&lt;p&gt;Here&apos;s a real-world example in React using the CSS-in-JS library Styled Components:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// declared in an external library
const Text = styled.p`
    color: lightblue;
`

// declared in an external library
const Card = ({ children }) =&amp;gt; (
    &amp;lt;SCard&amp;gt;
        &amp;lt;Text as=&quot;p&quot; color={default}&amp;gt;{children}&amp;lt;/Text&amp;gt;
    &amp;lt;/SCard&amp;gt;
)

// my override
const SCard = styled(Card)`
    p {
        color: green;
    }
`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, no inheritance will occur, because the style applied directly to &amp;lt;Text/&amp;gt; has higher priority than the color declaration on &amp;lt;SCard/&amp;gt;.&lt;/p&gt;
&lt;p&gt;So, how do I make the text in &amp;lt;SCard/&amp;gt; the colour I need it to be? At this point, I have to either set the API of the &amp;lt;Text/&amp;gt; component to recieve a color property (if you have access to that, which I didn&apos;t), or override the colour in &amp;lt;SCard/&amp;gt; using an !important declaration.&lt;/p&gt;
&lt;h3&gt;1: CSS inheritance: The solution &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#1:-css-inheritance:-the-solution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In this instance, our solution was to change the API of &amp;lt;Text /&amp;gt; so that by default the colour was inherit. This made it possible for both approaches: inheritance where necessary using CSS, or by adding a colour directly to the Text component using it&apos;s color property.&lt;/p&gt;
&lt;h3&gt;2: Large pages: the conundrum &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#2:-large-pages:-the-conundrum&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I&apos;m not sure what the average number of DOM elements on a page is. We have some pages that have a lot of elements, around 1,100. Just about every element is a separate React.Component, and due to design variation, we extend a lot of them in a similar way to this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import Card from &quot;component-library&quot;;

const SCard = styled(Card)`
  background: lightblue;
`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This seems to wrap the component in a React.Context that serves to keep track of the original component ... after all, if the original component changes, how will we know to update this extended component?&lt;/p&gt;
&lt;p&gt;On the pages where there are a lot of these extended components, we are seeing a significant drop in performance. I&apos;m assuming it&apos;s because of the extra dependencies involved (1. the original component, 2. the React.Context wrapper, 3. the new component).&lt;/p&gt;
&lt;h3&gt;2: Large pages: the solution(s) &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#2:-large-pages:-the-solution(s)&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We haven&apos;t solved this yet, but I found it particularly interesting that there was a significant drop in performance, proving to be a noticeable factor in how many conversions we see from those pages when compared to a previous iteration of the application.&lt;/p&gt;
&lt;p&gt;I can think of a few options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Switch to a css-in-js solution with lower (or zero) runtime, like Linaria&lt;/li&gt;
&lt;li&gt;Keep Styled Components but export CSS strings alongside the components so that we can extend them using CSS only (in most, the components&apos; APIs don&apos;t change significantly)&lt;/li&gt;
&lt;li&gt;Rationalise the design so that it uses styles that already exist in the library&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;What would you choose?&lt;/p&gt;
&lt;h3&gt;3: Markdown content: the conundrum &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#3:-markdown-content:-the-conundrum&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This one is related to the above, but we are also using these components in Markdown content we pull in from our CMS. So for each &amp;lt;p&amp;gt; tag that&apos;s generated from the Markdown, we use our libraries&apos; &amp;lt;Text /&amp;gt;component.&lt;/p&gt;
&lt;p&gt;This hasn&apos;t been an issue yet, but I can see one day we&apos;re going to have an extremely long page of content that uses many elements that also impacts page performance.&lt;/p&gt;
&lt;h3&gt;3: Markdown content: the possible solution(s) &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#3:-markdown-content:-the-possible-solution(s)&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We&apos;re probably going to have to use one of the 3 solutions in the previous conundrum to solve this one too.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/three-ui-component-conundrums/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have tremendous confidence in css-in-js, and believe if you&apos;re using React, Styled Components is a great, broadly performant solution that gets most people where they need to be.&lt;/p&gt;
&lt;p&gt;However, it has to be noted that there is a runtime overhead that increases with every component you extend.&lt;/p&gt;
&lt;p&gt;I am interested to see if there&apos;s a way of compiling all components at build time and avoiding that React.Context link, since in our case we don&apos;t expect the original components to change after that.&lt;/p&gt;
&lt;p&gt;And I&apos;m interested to see whether other solutions, like Linaria, can provide a solution we can utilise in the future.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Three UI Component Conundrums&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>To Rome, a Poem</title><link>https://deliciousreverie.co.uk/posts/to-rome-a-poem/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/to-rome-a-poem/</guid><description>I wrote this poem more than 10 years ago, but I&apos;ve never published it anywhere. I&apos;ve always been afraid it&apos;s not any good or that it&apos;s of not enough interest to warrant any merit ... but that seems to matter less now. Anyway, here it is! </description><pubDate>Mon, 23 Nov 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Monday, 23 November 2015&lt;/p&gt;
&lt;p&gt;I wrote this poem more than 10 years ago, but I&apos;ve never published it anywhere. I&apos;ve always been afraid it&apos;s not any good or that it&apos;s of not enough interest to warrant any merit ... but that seems to matter less now. Anyway, here it is!&lt;/p&gt;
&lt;p&gt;I wrote it after I read &lt;a href=&quot;https://www.amazon.co.uk/dp/0571172288&quot;&gt;Andrew Motion&apos;s biography of John Keats&lt;/a&gt;. I was so moved by the tragic life Keats had and how he&apos;d created some of the world&apos;s best poetry despite that.&lt;/p&gt;
&lt;h3&gt;To Rome &lt;a href=&quot;https://deliciousreverie.co.uk/posts/to-rome-a-poem/#to-rome&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&amp;lt;div class=&quot;[&amp;amp;&amp;gt;pre]:ml-0! [&amp;amp;&amp;gt;pre]:mr-0! [&amp;amp;&amp;gt;pre]:bg-transparent!&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;It&apos;s a rose you&apos;ve given, and a rose I&apos;ll take
  As I leave you awash in my wake.
Though I travel to distant shores
  I&apos;ll remember that rose that it was yours.

Now it&apos;s the third day out to sea
  And closer the shores of Napoli
Today I feel a lost cause
  Remembering that rose, that it was yours.

Never can an absolution be found
  This ship of ours has run aground
And climbing the steps to the Piazza&apos;s palace
  I lift to you a golden chalice:

Not Apollo&apos;s goblet, nor Endymion&apos;s cup
  Only for you would I offer up
The dregs become of this earthly root
  That my soul might sprout forth a shoot

And I might live again to see
  That rose you gave, you gave to me.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;p&gt;I imagined that when he left his lover and former fianceé, that she gave him a rose. But the fact is, Keats left the country without even saying goodbye to her, despite the deep emotional pain that caused him.&lt;/p&gt;
&lt;p&gt;I imagined him drinking a cup to his lover, but wanted to mention that it wasn&apos;t a glorious cup of a god like Apollo (Keats never saw himself that way), nor was it Endymion&apos;s (who lived forever).&lt;/p&gt;
&lt;p&gt;The dregs become of this earthly rootThat my soul might sprout forth a shoot&lt;/p&gt;
&lt;p&gt;These 2 lines are from the poem Endymion, by Keats, and allude to the way his romance with Fanny Brawne made him feel.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:To Rome, a Poem&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Tools for Production Ready Code</title><link>https://deliciousreverie.co.uk/posts/tools-production-ready-code/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/tools-production-ready-code/</guid><description>Originally produced for Net print Magazine. Websites are great tools for communication, but it&apos;s remarkably easy to take them offline. For those organisations in which there&apos;s a huge financial investment and thousands — perhaps millions - of customers, reliability is a huge concern. The strategies and tools we are looking at here cover both prevention and resolution of problems that could occur to any website. </description><pubDate>Wed, 14 Oct 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Wednesday, 14 October 2020&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A note about this article: This was a feature I wrote for Net magazine which was sadly closed down before it was published, so I published it on &lt;a href=&quot;http://dev.to/&quot;&gt;dev.to&lt;/a&gt; initially. It&apos;s aimed at people who want to get into devOps, or product owners who want to understand what it is they&apos;re getting themselves into. I hope it proves to be useful.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Websites are great tools for communication, but it&apos;s remarkably easy to take them offline. One of the most panic-inducing moments for any operation can be that first call from a customer informing you that the site is down ... and then the next call, and then fifty more ... suddenly the company grinds to a halt until the problem can be fixed.&lt;/p&gt;
&lt;p&gt;Downtime might not be much of a problem for every site out there. If you take down your own personal blog for a few hours, it probably doesn&apos;t matter a huge amount. However, most companies and other organisations now depend upon their web sites being accessible 24 hours a day, 7 days a week. Any downtime for these organisations results in loss: the company could be losing money, critical information might not be reaching the right people in a disaster zone, or at the very least, the organisation&apos;s reputation is being eroded, minute by minute.&lt;/p&gt;
&lt;p&gt;For those organisations in which there&apos;s a huge financial investment and thousands — perhaps millions - of customers, reliability is a huge concern. There are now a large range of tools, products and - above all - strategies - for ensuring that if (or rather, when) something untoward happens, the right people can resolve the situation as quickly as possible.&lt;/p&gt;
&lt;p&gt;In this article we&apos;re going to provide some answers to these questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How can we as development teams continue to offer high levels of confidence?&lt;/li&gt;
&lt;li&gt;How can we trust that new features are going to work as expected?&lt;/li&gt;
&lt;li&gt;What can we do to minimise issues that do occur?&lt;/li&gt;
&lt;li&gt;What steps can we take to make sure any unwanted surprises can be fixed quickly?&lt;/li&gt;
&lt;li&gt;How can we make sure that the duration of any downtime is as short as possible?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We have at our fingertips a wealth of tools that can be added to a code base, and a process flow, to catch issues before they reach production ... and if they do reach production, to minimise the impact.&lt;/p&gt;
&lt;p&gt;The strategies and tools we&apos;re looking at here cover both prevention and resolution of problems that could occur to any web site, but with a focus on full-stack JavaScript.&lt;/p&gt;
&lt;h2&gt;Situation: When JavaScript fails &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-javascript-fails&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript is a dynamic language. It&apos;s great for coercing strings into numbers and back again. But sometimes this causes issues. We have to make sure that the values we&apos;re working with are of the same type, otherwise one error in the JavaScript code could break the site entirely.&lt;/p&gt;
&lt;p&gt;There have been a number of notable projects that make an effort to deal with this kind of problem by turning JavaScript from a dynamic to a statically typed language:&lt;/p&gt;
&lt;h3&gt;TypeScript, Flow or Reason &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#typescript-flow-or-reason&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;TypeScript and Flow are the two most popular ways of turning JavaScript into a statically-typed language. Both approaches are a superscript of JavaScript, which means your code needs to be transpiled back into normal JavaScript in order to work on the web.&lt;/p&gt;
&lt;p&gt;TypeScript is run by Microsoft and is developed for building applications at scale. Flow is a great alternative with a similar api.&lt;/p&gt;
&lt;p&gt;Reason is a more interpretive system that allows you to write very succinct type-safe code. There are a lot of features to this language that mean it can appear quite different to the JavaScript developers are used to writing.&lt;/p&gt;
&lt;p&gt;Neither tool is perfect: each of these languages still needs to be compiled to the kind of JavaScript that runs in browsers. Developers need to also adopt strategies to ensure that compiled code still coerces values correctly by writing functions that check types where it could be of an unexpected type, for example data being requested from a 3rd party via an API.&lt;/p&gt;
&lt;h2&gt;Situation: When things aren&apos;t working as expected &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-things-aren&apos;t-working-as-expected&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The development team stand back proudly, showing you the work they&apos;ve done over the last three months. Their pride quickly melts into dismay as the stakeholders say, &quot;It wasn&apos;t supposed to work like that...!&quot; Suddenly, the team has some costly refactoring work to do.&lt;/p&gt;
&lt;p&gt;Assumptions are a development project&apos;s worst enemy. The more assumptions we make about how something is meant to work, the more risk we are creating. The best way to avoid assumptions is by investing time in gathering requirements and writing up clear documentation before any development work starts. If the documentation is loose, unclear or even non-existent, there will be a higher chance of something not working as expected.&lt;/p&gt;
&lt;p&gt;&quot;“Hell is other people’s undocumented assumptions.” — Nicole Fenton&lt;/p&gt;
&lt;h3&gt;Project Management Tools &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#project-management-tools&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Project management tools such as Trello, JIRA, Monday and others can be set up so that requirements can be clearly documented before project work can begin, and work can be broken down into manageable chunks. Some things that should be included in the documentation should include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Feature requirements&lt;/li&gt;
&lt;li&gt;Scope (what it should not include)&lt;/li&gt;
&lt;li&gt;UI Mockups&lt;/li&gt;
&lt;li&gt;Testing approach&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Documenting the features as a team can be instrumental in reducing undocumented assumptions, allowing the developer to write code relevant to solving the task in scope.&lt;/p&gt;
&lt;p&gt;For more information on this strategy, see the box &quot;Documenting Features&quot; below.&lt;/p&gt;
&lt;h2&gt;Supplementary info: Documenting Features &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#supplementary-info:-documenting-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are able to find an issue early in the process then it costs far less time to fix than if you found it later on. For this reason, good documentation is key to having production ready code.&lt;/p&gt;
&lt;p&gt;Documenting a feature should include acceptance criteria, which forms the basis for integration or E2E tests, and can be written in the form of given - when - then statements, for example:&lt;/p&gt;
&lt;p&gt;Given I am using the websiteWhen I visit the signup pageThen I can create an account&lt;/p&gt;
&lt;p&gt;Here&apos;s another:&lt;/p&gt;
&lt;p&gt;Given I am using the websiteWhen I visit the signup page and have forgotten my passwordThen I can reset my password&lt;/p&gt;
&lt;p&gt;The first part of the statement, Given, may sound a bit redundant at first, and it could be tempting to miss it out altogether. But it really helps to frame the feature and can become very useful as a reference point further on in the process.&lt;/p&gt;
&lt;p&gt;If the E2E tests fail against these acceptance criteria, the feature can be fixed or rethought.&lt;/p&gt;
&lt;h3&gt;Unit tests &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#unit-tests&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unit tests using a testing library (such as Jest, Mocha, Chai, Jasmine or perhaps something else) are the foundation of developers being able to ensure things are working as they should. How you adopt this approach can vary, but production-ready code means there are at least tests for each possible outcome of the component.&lt;/p&gt;
&lt;p&gt;TDD, or test-driven development, means that a developer will write tests before they write the component they are building. This helps with conceptual thinking because the developer can have each outcome clearly in mind before they start writing code. This often results in less code refactors, and more optimal code that doesn&apos;t have as much legacy or unnecessary lines.&lt;/p&gt;
&lt;p&gt;For TDD to be effective, the component to be built needs to be planned carefully first.&lt;/p&gt;
&lt;h2&gt;Situation: When your users do something you didn&apos;t expect &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-your-users-do-something-you-didn&apos;t-expect&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The phrase &quot;expect the unexpected&quot; is most apt when used in connection with software and those who use it! Quite often, people using your software will be able to use things in a way you didn&apos;t anticipate. The best way to avoid this is to invite people to use the software before it&apos;s released to everyone.&lt;/p&gt;
&lt;h3&gt;Quality Assurance testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#quality-assurance-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;QA Engineers can be viewed as the secret superpower of a development team. It&apos;s surprising how many times a good QA Engineer has saved a developer from themselves, or saved the development team from its own assumptions ... or even saved an entire business from its own good intentions. An on-hand QA Engineer is invaluable to a development team, but there are organisations you can contract who will run through your app and uncover issues you would never have been able to think up.&lt;/p&gt;
&lt;p&gt;Two of those companies are Global App Testing and User Testing. Both only have subscription plans. There might be others who can offer a one-off test of your app on a single payment basis.&lt;/p&gt;
&lt;h3&gt;User acceptance testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#user-acceptance-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some companies have a &quot;UAT&quot; environment that serves as a staging ground for code that is a candidate for release to the production site. They can then use that environment to organise a group of people to come in from the public to give their opinions on a feature to be released. This gives some qualitative assurance that the feature works and is going to benefit the intended audience. There are a number of companies who organise the individuals to come and test the software, which would involve a fee for their time.&lt;/p&gt;
&lt;p&gt;To be safest, it&apos;s best that everything in the stack - the database, the external functions, the frontend, the authentication server, is duplicated for UAT. That way, the complete user journey can be tested in isolation from production. The result is that any errors in the code won&apos;t accidentally knock out a production service. UAT should be a safer environment for developers to take necessary risks.&lt;/p&gt;
&lt;h3&gt;Multivariate, or A/B, Testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#multivariate-or-ab-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As well as qualitative testing (validating by quality), quantitative testing (validating by quantity) is also a good way of decreasing risk that a new feature could be causing a problem. Once Google Optimize, Optimizelyor a similar tool are integrated with your site, you can release a new feature to a segment of your potential customers for a set period of time.&lt;/p&gt;
&lt;p&gt;Once there has been a significant enough amount of traffic, you can analyse the results and check if conversions are increasing, decreasing, or have stopped altogether, and if the results aren&apos;t positive, you can turn it off so that no further losses occur.&lt;/p&gt;
&lt;h3&gt;User behaviour monitoring &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#user-behaviour-monitoring&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Getting an insight into user behaviour is invaluable to any experience. Tools like Hotjar, sessioncam, CrazyEgg and others record where the user clicks, and what journey they took through your site, which is particularly useful when introducing new user flows or features. However, these tools have a performance cost, and in my view shouldn&apos;t be left on indefinitely.&lt;/p&gt;
&lt;p&gt;Analytics tools such as Fathom, Google Analytics and others can also be a good source of insight into what could be breaking user flow. Some can even be set up to report activity you want to be monitored, for example if the number of 404 or 500 errors increases above a certain amount in a day or hour.&lt;/p&gt;
&lt;h2&gt;Situation: When components clash &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-components-clash&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The components we build for our websites might be perfectly built in themselves ... but what happens when we mix them with other components? This can often cause unexpected side effects that can crash your site or result in other issues or errors.&lt;/p&gt;
&lt;p&gt;For this reason, it&apos;s best to check beforehand how they integrate with other components, and whether your visitors can actually complete the tasks you want them to.&lt;/p&gt;
&lt;h3&gt;Integration testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#integration-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Often written in the same way as unit tests (see above), integration tests can be written for several components that are known to be used together, for example: input fields and a submit button.&lt;/p&gt;
&lt;h3&gt;End-to-end testing &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#end-to-end-testing&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tools like Selenium (which is cross-platform) and Cypress (JavaScript) can test a user flow from beginning to end (hence the name). This type of tool is critical to ensuring users can complete their journey from first visiting a site, to checking out and paying for a product, or any other journey you might want someone to take.&lt;/p&gt;
&lt;p&gt;End-to-end tests can often be written by the developers but the journey, and each possible outcome, need to be planned by the development team.&lt;/p&gt;
&lt;h2&gt;Situation: When you need to roll back quickly &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-you-need-to-roll-back-quickly&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Exceptions in an application that are not caused by malicious intent or load are often caused by new releases. Debugging in production isn&apos;t something you ever want to do, so once the cause has been identified it&apos;s wise to roll back to the previous release. Then the development team can continue to work on a fix without impeding the business.&lt;/p&gt;
&lt;p&gt;Tools that store each built version of an application, such as JFrog&apos;s Artifactory, can ensure that the time to rollback is kept to the absolute minimum. When this has been configured, you don&apos;t need to rebuild the version you&apos;re rolling back to, which could take from several minutes to potentially hours.&lt;/p&gt;
&lt;h2&gt;Situation: When you need to understand what the problem is &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-you-need-to-understand-what-the-problem-is&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The development team won&apos;t be able to fix the issue unless they clearly understand the problem ... that&apos;s why it&apos;s important to implement different types of monitoring, both on the app, and separately too.&lt;/p&gt;
&lt;h3&gt;Application Reporting &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#application-reporting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Logging errors to the browser console is the most basic kind of application reporting, but that limits debugging to only the issue that occur on one person&apos;s machine ... what if you want to see what happened on someone else&apos;s?&lt;/p&gt;
&lt;p&gt;Tools such as Sentry, LogRocket, BugSnag and a plethora of others, allow you to send errors to their platform, and from there to Slack, Email or to a telephone system.&lt;/p&gt;
&lt;p&gt;This way, whenever there is a serious error in production, the appropriate people can be notified immediately.&lt;/p&gt;
&lt;p&gt;However, some issues are not immediately obvious and require more monitoring over longer time periods.&lt;/p&gt;
&lt;h3&gt;Application Monitoring &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#application-monitoring&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tools like Splunk, DataDog, Honeycomb or AWS X-Ray allow you to log other useful data so that you can closely monitor the activity of your application or distributed systems.&lt;/p&gt;
&lt;p&gt;A word of caution: it&apos;s important to be careful that no personal data is being logged without users being informed and consent obtained.&lt;/p&gt;
&lt;p&gt;Knowing what needs to be logged and what doesn&apos;t takes time to discover: be prepared to keep adapting your strategy until you log useful data ... and only the useful data!&lt;/p&gt;
&lt;h3&gt;Health reporting &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#health-reporting&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Of course, if your site is throwing a server error (HTTP 500 code), no amount of in-app logging is going to inform you of that: your web app is down! For that reason, external services can help monitor the health of your app, and inform you when something happens.&lt;/p&gt;
&lt;p&gt;One of the most basic of these is Uptime Monitor, which pings a site at regular intervals. There are more sophisticated tools like AWS Cloudwatch, Pingdom, Retrace and others which allow you to analyse your site in real time and send out alerts to your team.&lt;/p&gt;
&lt;p&gt;Some also take the view that automated QA tests should run in production too, so that you can fully grasp the state of the production app. This is really helpful especially if you can&apos;t replicate your testing environment identically like your production environment. But we need to be careful: if we have a monolithic app, the extra load created by the automated tests could cause crashes at peak times. If it&apos;s a serverless app, you could find you&apos;ve incurred extra costs for runtime.&lt;/p&gt;
&lt;h2&gt;Situation: When you&apos;re under attack &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#situation:-when-you&apos;re-under-attack&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This kind of issue is out of scope for this article, but I feel it&apos;s worth mentioning a valuable resource that has become the go-to handbook for modern DevOps teams, and is highly valued for its insights into the way Google manages applications at vast scale: the Site Reliability Handbook, published by O&apos;Reilly Media. It&apos;s available to read online here: &lt;a href=&quot;https://landing.google.com/sre/sre-book/toc/index.html&quot;&gt;https://landing.google.com/sre/sre-book/toc/index.html&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Supplementary info: How to Manage Incidents &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#supplementary-info:-how-to-manage-incidents&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Despite everyone&apos;s best efforts, failures in production will occur. There are strategies and tools to help reduce the downtime and get your services back up and running more quickly:&lt;/p&gt;
&lt;h3&gt;Step 1. Define a severity level &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#step-1.-define-a-severity-level&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When an incident occurs, it&apos;s everyone&apos;s first priority to get things working again. However, defining different levels of severity helps everyone understand the impact it&apos;s having on customers and the organisation: some incidents will naturally have higher impact than others; some will have no immediate impact but could still require an incident level response.&lt;/p&gt;
&lt;h3&gt;Step 2. Delegate and manage communication &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#step-2.-delegate-and-manage-communication&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Having a dedicated incident management team is invaluable when there are issues in production. Otherwise, someone should be assigned to keep stakeholders and other interested parties up to date with what&apos;s happening. Splitting this responsibility away frees the developers to focus on fixing the problem and informs other developers who are not involved to stop them from jumping in with well-meaning patches — patches that could make the situation worse.&lt;/p&gt;
&lt;h3&gt;Step 3. Arrange post mortem meetings &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#step-3.-arrange-post-mortem-meetings&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once a situation has been resolved, post mortem meetings can be very helpful to identify what the problem was and put things in place to avoid a similar issue from arising again. Such meetings aren&apos;t focused on blame; rather they should be about understanding the problem and how to avoid it happening in future.&lt;/p&gt;
&lt;h2&gt;Supplementary info: A word about code reviews &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#supplementary-info:-a-word-about-code-reviews&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of the best ways of catching a range of potential issues early on in the development process is by the use of effective code reviews. Having two or more developers that are familiar with a codebase and that use it regularly review each other&apos;s code means it&apos;s going to reflect a more well-thought out and well-structured project. Good code reviews can also point out flaws in logic and gaps in tests that might easily slip through without notice if there was just one developer looking after a project.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/tools-production-ready-code/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The web is still a new frontier, the boundaries of which we may never fully define. Its great strength is in diversity and potential to be used in a variety of ways: from just one individual to a small family unit; for a community or enterprise, to a tool for the betterment of humankind.&lt;/p&gt;
&lt;p&gt;But with great power comes great responsibility. I hope some of the strategies we&apos;ve looked at, and the tools to mitigate them, help your organisation to provide stable, predictable production-ready code in the face of ever-changing challenges of the expanding, mutating and growing world wide web.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Tools for Production Ready Code&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Towards better hydration</title><link>https://deliciousreverie.co.uk/posts/towards-better-rehydration/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/towards-better-rehydration/</guid><description>I came across a tool recently that I&apos;m really excited about ... it&apos;s something that I think can solve one of the most pressing concerns in JavaScript development: rehydration. </description><pubDate>Sat, 08 May 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Saturday, 8 May 2021&lt;/p&gt;
&lt;p&gt;I came across a tool recently that I&apos;m really excited about ... it&apos;s something that I think can solve one of the most pressing concerns in JavaScript development: rehydration.&lt;/p&gt;
&lt;p&gt;Imagine how orange juice is transported: the water is removed to reduce the quantity, and water is added back - &quot;rehydrated&quot; - at the destination.&lt;/p&gt;
&lt;p&gt;A similar thing happens with a subset of modern JavaScript frameworks: they build static HTML pages (either when the application is built or on the server) send that down to the browser. Then they send and parse a JavaScript bundle that also contains the layout of the page, except this one is interactive (hence &quot;rehydrated&quot;). It&apos;s that JavaScript generated representation of the HTML you see when you visit the site.&lt;/p&gt;
&lt;p&gt;These frameworks have really caught on because they allow for the greatest expressiveness possible without splitting concerns: they allow developers to write components instead of separate HTML, CSS and JavaScript.&lt;/p&gt;
&lt;p&gt;But I think there is a fundamental problem with this idea that I think needs to be solved before we can scale our applications for the next generation of frontend architecture.&lt;/p&gt;
&lt;p&gt;I have just come across a tool that makes me think we&apos;re much closer to finding a suitable answer to this significant issue.&lt;/p&gt;
&lt;h2&gt;The issue &lt;a href=&quot;https://deliciousreverie.co.uk/posts/towards-better-rehydration/#the-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;JavaScript is inextricably tied to the other building blocks of the web: html and css. There&apos;s one single line of code that has allowed more and more control to be given to JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const root = document.getElementById(&quot;root&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you&apos;ve spent any time with JavaScript, you&apos;ll recognise that this is where it takes over the DOM, rendering pages in JavaScript instead of native HTML, facilitating our single page applications.&lt;/p&gt;
&lt;p&gt;But this creates a few issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nobody can see the page until the JavaScript is parsed and that version of the page renders (even if we&apos;re using static or server rendering),&lt;/li&gt;
&lt;li&gt;Bundle sizes grow larger and larger dependent on the requirements of the application, meaning that the JS pages take longer dependent on the bundle size (and therefore the complexity of the project),&lt;/li&gt;
&lt;li&gt;We also create performance issues such as time to interactive delays, or poor interactivity for people on lower-powered devices, because JavaScript runtime takes more processing power (not to mention battery energy) than plain old HTML.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There have been three main ideas around how to solve this up until now:&lt;/p&gt;
&lt;h2&gt;Solution 1: Static all the way ... &lt;a href=&quot;https://deliciousreverie.co.uk/posts/towards-better-rehydration/#solution-1:-static-all-the-way-...&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first answer to this problem is static-site generation: use JS to compile in the pipeline, and serve only static HTML to the browser. Certain static-site generators (like Hugo, Jekyll, Eleventy etc), excel at this.&lt;/p&gt;
&lt;p&gt;The advantage of this approach is that HTML is still the fastest and best way to serve up content.&lt;/p&gt;
&lt;p&gt;However, when you want to add any dynamic functionality, you need to load on a separate JavaScript framework, like Vue, Alpine or React. Some static site generators bundle this functionality on, some just focus on statically generating pages.&lt;/p&gt;
&lt;p&gt;I think the problem still remains: the static site and the javascript are separate entities, and it could sometimes be challenging to keep them in sync.&lt;/p&gt;
&lt;h2&gt;Solution 2: Rehydrate all the things &lt;a href=&quot;https://deliciousreverie.co.uk/posts/towards-better-rehydration/#solution-2:-rehydrate-all-the-things&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In sharp contrast to the static generators above, Next, Nuxt, Gatsby, SvelteKit and others send down HTML and then rehydrate the entire page in JavaScript.&lt;/p&gt;
&lt;p&gt;This has some cool advantages, such as literally no overhead when making components dynamic or interactive, because, at the end of the day, the whole page is already rendered in JavaScript.&lt;/p&gt;
&lt;p&gt;But ... and this is a big but! ... it&apos;s expensive.&lt;/p&gt;
&lt;p&gt;Every page has both HTML and JavaScript counterparts. This means bundle sizes are larger and browsers are working harder to process JavaScript for scroll position, routing and even CSS styles.&lt;/p&gt;
&lt;h2&gt;Solution 3: Splitting traffic &lt;a href=&quot;https://deliciousreverie.co.uk/posts/towards-better-rehydration/#solution-3:-splitting-traffic&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve been trying to figure out how we negate these problems, because until we do, we face an impasse which prevents us from scaling our applications as we want — and from reaching broader audiences.&lt;/p&gt;
&lt;p&gt;(Remember, the next billion web users come from countries where conditions are a lot less ... guaranteed).&lt;/p&gt;
&lt;p&gt;My attempt at this idea is what I currently do on my website: I allow people the option to visit a statically-rendered HTML and CSS site, or to visit a JavaScript SPA rendered with Preact.&lt;/p&gt;
&lt;p&gt;There&apos;s a variation on this one which I&apos;ve been thinking about on and off for a while: using an edge handler (such as those Cloudflare or Cloudfront provide, short running standalone functions) to split traffic (or transform websites even before they get to the viewer) based on some sort of browser preference setting. With this idea, we could route people based on one of these three settings:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;device has low battery&lt;/li&gt;
&lt;li&gt;device has a poor network connection&lt;/li&gt;
&lt;li&gt;user has enabled a setting preferring less JavaScript&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The trouble with each of these in turn is that (1) battery status can be used to track people against their wishes, (2) the state of the network isn&apos;t typically known until the site has finished downloading, and (3) this one gets awfully muddy awfully quickly: all JavaScript? That would be like saying turn off the browsers&apos; ability to drop cookies: loads of services normal people use every day would be useless.&lt;/p&gt;
&lt;p&gt;So no, splitting traffic isn&apos;t a good option. It may be fine to do that on small blogs like this one, but it&apos;s not a viable option for sites when some complex functionality is required .. or even some simple functionality: my HTML-only site doesn&apos;t have animations or a search tool.&lt;/p&gt;
&lt;h2&gt;A solution &lt;a href=&quot;https://deliciousreverie.co.uk/posts/towards-better-rehydration/#a-solution&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What we need is something in-between these options. Something that can build HTML statically (or on the server), and partially rehydrate elements on the client where that is needed.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This isn&apos;t a new idea. The core React team were hoping that concurrent mode would allow you to do this. However, building up a DOM tree is pretty difficult to achieve asynchronously.&lt;/p&gt;
&lt;p&gt;Massive steps towards this idea have been made by NextJS with server rendering (building pages on demand), and with SvelteKit&apos;s optional rendering (which allows individual pages to be statically rendered as HTML or JS) ... but it&apos;s still happening on a per-page basis, meaning that for those pages: there&apos;s either no possibility of interactivity, or there&apos;s a JavaScript overhead. Sometimes both.&lt;/p&gt;
&lt;p&gt;This idea of partial rehydration, or &quot;Islands architecture&quot; by Jason Miller (creator of, among other things, Preact), is something I had really hoped we&apos;d get to at some point.&lt;/p&gt;
&lt;p&gt;And now it seems we are here. &lt;a href=&quot;https://www.youtube.com/watch?t=10490&amp;amp;v=fnr9XWvjJHw&amp;amp;feature=emb_imp_woyt&quot;&gt;This talk at Svelte Summit 2021 blew my mind.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Astro is the first tool I&apos;ve come across that has this ambition: to facilitate importing and selectively hydrating components in your page as you require them.&lt;/p&gt;
&lt;p&gt;And Astro goes further: we can share state across components, or potentially use different modern frameworks (react, svelte, Angular, you name it!) as we want, and we can request JavaScript to only load when the component is in the viewport.&lt;/p&gt;
&lt;p&gt;This has the potential to solve all of these problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No more &quot;uncanny valley&quot; of pages being visible but not being interactive&lt;/li&gt;
&lt;li&gt;No large bundles being loaded upfront&lt;/li&gt;
&lt;li&gt;We could even give the user control over whether they want to load certain components&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Astro is in early alpha, and seemingly in stealth mode too! I&apos;m so eager to get building with this tool now, but it seems I&apos;m going to have to wait a while before the team are ready to open it to a public beta offering.&lt;/p&gt;
&lt;p&gt;This could be a really powerful tool, and fundamentally improve the way we build for the web. How exciting would that be!&lt;/p&gt;
&lt;p&gt;PS. &lt;a href=&quot;https://astro.build/&quot;&gt;slightly more detail here on Astro&apos;s website&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;PPS. &lt;a href=&quot;https://css-tricks.com/astro/&quot;&gt;Of course Chris Coiyer has already written a post on CSS Tricks about Astro&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Towards better hydration&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Tracking video plays</title><link>https://deliciousreverie.co.uk/posts/tracking-video-plays/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/tracking-video-plays/</guid><description>The scope of this project was to track how much of a video has been watched periodically, send the data to an API at regular intervals. Here&apos;s how I achieved that in a developer-friendly way.</description><pubDate>Thu, 16 Nov 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;The scope of this project was to track how much of a video has been watched periodically, send the data to an API at regular intervals, to be able to track different video providers (YouTube, Vimeo and others), and to track multiple videos on a page, all of which could be playing simultaneously.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It was a little tricky because each player sends different events in the course of a video play, for example when the video has been paused, that we would need to respond to.&lt;/p&gt;
&lt;p&gt;Also to provide continuous tracking we sometimes need to call the player to return data, such as the video title (which can sometimes change mid-play), and the duration of play that has elapsed.&lt;/p&gt;
&lt;p&gt;This was a rebuild project became necessary because the original codebase was very difficult to modify. It had the following issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The code had been written very poorly&lt;/li&gt;
&lt;li&gt;There were no tests or types&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In addition to the above, I wanted to make the new tracker with such good developer experience that it would be as easy as possible to understand the code. The aim was to leave it open to modification by other developers. This would increase the longevity of the tracker and reduce the time investment over the lifecycle of the project.&lt;/p&gt;
&lt;h2&gt;First Principles&lt;/h2&gt;
&lt;p&gt;We had already decided to adopt JSDoc instead of TypeScript for our codebase. &lt;a href=&quot;https://deliciousreverie.co.uk/posts/types-via-jsdoc-or-typescript/&quot;&gt;I wrote a separate article about this if you&apos;re interested&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition, I have been standardising all of our codebases to use ESModules and Vite, so it made sense to opt for Vitest for our testing suite across all projects.&lt;/p&gt;
&lt;p&gt;The other thing I wished to change was that this script was  hosted on a domain as a text file, and from there pulled into applications that needed it. I didn&apos;t think this was robust enough for such a key part of our business, so after discussion we decided to bundle it as a package.&lt;/p&gt;
&lt;p&gt;This meant it could be versioned, and any rollbacks that were necessary could be done on a per-application basis and fixed forward instead of rolling back the tracker for every application simultaneously.&lt;/p&gt;
&lt;p&gt;The other advantage to this is that changes can be rolled out gradually instead of launching on all of our codebases at the same time.&lt;/p&gt;
&lt;p&gt;The application would also not be bound to the global &lt;code&gt;window&lt;/code&gt; scope. I didn&apos;t see any advantage to doing that. Instead, it would be called in the consuming application and maintain it&apos;s own context.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;With those rules established, I implemented the initial API for calling the tracker, which would look something like this for a HTML5 video:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;video id=&quot;my-video&quot;&amp;gt;
  &amp;lt;source src=&quot;video.mp4&quot;&amp;gt;
&amp;lt;/video&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You would initiate tracking by passing the element&apos;s &lt;code&gt;id&lt;/code&gt; and the video type, in this case:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tracker(&apos;my-video&apos;).setup(&apos;html5&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&apos;m not giving away too much here deliberately; there&apos;s a bunch of other stuff happening in the &lt;code&gt;tracker&lt;/code&gt; function like plugins, event listeners and such, which are out of scope for this article.&lt;/p&gt;
&lt;p&gt;The main object of this function is to match the type of player (we call them &quot;providers&quot; for disambiguation) with specific code needed to listen to events from that player.&lt;/p&gt;
&lt;p&gt;Our &lt;code&gt;setupProvider&lt;/code&gt; contains an object that is keyed to all of our providers, and dynamically imports the configuration for that provider, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const providers = {
	youtube: async(elementId) =&amp;gt; {
		const addYouTubeTracking = await import(&quot;./providers/add-youtube-tracking&quot;)
		addYouTubeTracking.default(elementId)
	}
	vimeo: async(elementId) =&amp;gt; {
		...
	}
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it very easy to add a new provider. You need only instantiate the code for that player and add the appropriate designation to this object.&lt;/p&gt;
&lt;p&gt;In the parent function we can parse this object and if there&apos;s no match inform the user:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function setupProvider(provider, id) {
	const supportedProviders = Object.keys(providers)
	if(!supportedProviders.includes(provider)) {
		// handle the error
	}
	providers[provider](elementId)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I find object literals with this catch pattern very practical; as well as &lt;a href=&quot;https://www.north-47.com/javascript-objects-why-how-compared-with-switch-case-and-if/#:~:text=Since%20the%20objects%20approach%20is,an%20old%20way%20of%20coding&quot;&gt;being more performant&lt;/a&gt;, it&apos;s easier to catch errors and results in less duplication of code.&lt;/p&gt;
&lt;p&gt;Next, we set up the &lt;code&gt;addYouTubeTracking()&lt;/code&gt; function to listen to events from that player. I leveraged the &lt;a href=&quot;https://www.npmjs.com/package/youtube-player&quot;&gt;YouTube Player API&lt;/a&gt; abstraction for querying the player:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
function addYouTubeTracking(elementId){
	// Check if the element exists in the DOM
	const element = document.querySelector(`${elementId}`)
	if(!element) {
		// handle error
	}
	// set up YouTube API
	player.loadVideoByUrl(element.src)
	player.on(&quot;ready&quot;, function() {
		// See below for details
		playerEvents(&quot;ready&quot;, elementId)
	})
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instantiating providers separately like this not only avoids having to load code that isn&apos;t used in our application but it also allows us to standardise an API for talking to the rest of the application.&lt;/p&gt;
&lt;p&gt;After this function, we have two significant pieces: &lt;code&gt;playerEvents()&lt;/code&gt;, which can respond to events from every player in a uniform fashion, and &lt;code&gt;dataEvents()&lt;/code&gt; which collates the data and passes it to an API.&lt;/p&gt;
&lt;h2&gt;Responding to player events&lt;/h2&gt;
&lt;p&gt;To enable &lt;code&gt;playerEvents()&lt;/code&gt; to do this, each &lt;code&gt;add[provider]Tracking()&lt;/code&gt; passes down events and also a group of callback functions that provide standardised APIs for getting the title of the video, the elapsed duration and other useful information. It means we can call the provider and get accurate data as the play is progressing without having to wire them all up individually: the &lt;code&gt;playerEvents()&lt;/code&gt; call looks more like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const playerFunctions = {
	getElapsedDuration: () =&amp;gt; player.getCurrentTime()
}

playerEvents(&quot;ready&quot;, elementId, playerFunctions)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since the nomenclature of the elapsed duration varies from player to player, I&apos;m handling that variation here in the same context. This saves developers from the mental overhead later, when they should only be concerned with the next stage of the application.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;playerEvents()&lt;/code&gt; function I follow the same pattern as in &lt;code&gt;setupProvider()&lt;/code&gt;, with an object enumerating the events and errors in case of unhandled events.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of how we handle an event:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;play: () =&amp;gt; {
  dataEvents(&quot;update&quot;, elementId, {
    action: &quot;play&quot;,
    updated: Math.floor(Date.now() / 1000),
  });
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, there&apos;s a lot more in this function that I&apos;m not telling you about. Not shown is how use a &lt;code&gt;setInterval()&lt;/code&gt; on a &lt;code&gt;play&lt;/code&gt; event so we can call back to the provider and update the elapsed duration from the provider.&lt;/p&gt;
&lt;p&gt;Other events that could be sent from the player are also handled here include &lt;code&gt;seek&lt;/code&gt; (our user has skipped forward or backwards), &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;pause&lt;/code&gt; etc.&lt;/p&gt;
&lt;h2&gt;Parsing data events&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dataEvents()&lt;/code&gt; follows the same pattern as the two previous functions. Yeah, I really like this method of indexing. But it&apos;s also practical: if a new developer comes into this code they should more easily be able to understand all of the logic in the application flow.&lt;/p&gt;
&lt;p&gt;If it&apos;s a &lt;code&gt;play&lt;/code&gt; event, we actually send an &lt;code&gt;update&lt;/code&gt; event, since we should have already been sent a &lt;code&gt;new&lt;/code&gt; event when the provider declared it was ready.&lt;/p&gt;
&lt;p&gt;Just in case though, we check whether we have already collected data from this tracked video, and if not we call the &lt;code&gt;new&lt;/code&gt; function to instantiate a new set of data.&lt;/p&gt;
&lt;p&gt;How do we check whether we&apos;ve heard from this tracked video before? Aha, this is one of my favourite pieces.&lt;/p&gt;
&lt;h2&gt;One Direction for Data&lt;/h2&gt;
&lt;p&gt;No that&apos;s not the name of some electronic boy band.&lt;/p&gt;
&lt;p&gt;To be able to maintain a unidirectional data flow, I use the browser &lt;code&gt;cache&lt;/code&gt; API to store events that come in.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    update: async () =&amp;gt; {
      // do we already have a cache object for this?
      const cache = await caches.open(url);

      const cachedResponse = await cache.match(url);
		
		// looks like a no, let&apos;s create an initial event object
      if (!cachedResponse || !cachedResponse.ok) {
        dataEvents(&quot;new&quot;, elementId, null, incomingIndexes);
        return;
      }
      // if there&apos;s no old data or no valid data, we need to start over too
      const oldData = await cachedResponse?.json();
      const hasValidData = Object.keys(oldData[0]).length;
      
      if (!oldData || !hasValidData) {
        dataEvents(&quot;new&quot;, elementId, null, incomingIndexes);
        return;
      }

      // if the data is more than 4 hours old, we also want to start over
      const fourHoursAgo = Date.now() - 1000 * 60 * 60 * 4
      const isRecentData = fourHoursAgo &amp;gt; oldData[0].created;

      if(!isRecentData) {
      		// delete existing records
        cache.delete(url);
        // set it up again
        dataEvents(&quot;new&quot;, elementId, null, incomingIndexes);
        return;
      }

		// ok, we&apos;re good to go
		// This function overwrites the object retrieved from the cache with new values we obtained from the tracked event
      const outgoingIndexes = modifyTrackingData(
        oldData,
        updatedData,
      );
      
      // drop it into the cache
      cache.put(url, new Response(JSON.stringify(outgoingIndexes)));
		// Allamaraine!
      sendData(outgoingIndexes);
    },
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintaining data in the cache comes with a warning. I provide a unique &lt;code&gt;url&lt;/code&gt; string for the data object, and delete if it&apos;s more than 4 hours old. But even then we will have to be a little careful about when we release API changes.&lt;/p&gt;
&lt;p&gt;However the advantages are clear: we can provide all of the data required by the API, updating it as the video progresses, and we&apos;re not jumping through the hoops of creating a new tracked event index each time.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It&apos;s been really fun to use some new browser APIs on this project and to stretch the limits of my knowledge, particularly on how to handle events and different video platforms.&lt;/p&gt;
&lt;p&gt;But what I really enjoyed was making this code with future me, and others, in mind. I hope that it will make this code robust enough (along with the tests and types I&apos;ve created) so that it will last a long time and be malleable enough to fulfil other business needs in the future.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Tracking video plays&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How to scope Typecheck to your project folder</title><link>https://deliciousreverie.co.uk/posts/typecheck-source-only/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/typecheck-source-only/</guid><description>Typecheck should only run against the code you write for your project, but I found it&apos;s not that obvious to configure that... </description><pubDate>Tue, 31 Mar 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Tuesday, 31 March 2020&lt;/p&gt;
&lt;p&gt;I configured a command so that I can run a type check on my project, but the results always include stuff in the node_modules folder. It took me a lot of investigation and asking around before I figured out how to scope tsc to only the ./src/ folder?&lt;/p&gt;
&lt;p&gt;in package.json:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts: {
  &quot;typecheck&quot;: &quot;tsc --noEmit&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this configuration, he compiler should pick up config from tsconfig.json, which looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
  ...
  },
  &quot;include&quot;: [&quot;src/**/*&quot;],
  &quot;exclude&quot;: [&quot;node_modules&quot;, &quot;**/*.test.*&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But I still get results from node_modules when I run the command. It took me ages to figure it out, but for anyone else coming across this one, I managed to find that submitting an empty array to types did it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;compilerOptions&quot;: {
    &quot;types&quot;: []
}
```&quot;&lt;/code&gt;&lt;/pre&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How to scope Typecheck to your project folder&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Is JSDoc Better Than TypeScript? A Real World Example</title><link>https://deliciousreverie.co.uk/posts/types-via-jsdoc-or-typescript/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/types-via-jsdoc-or-typescript/</guid><description>I&apos;ve become an avid fan of TypeScript in recent years as it&apos;s improved my output, tightened up my appreciation for JavaScript&apos;s type coercion, and generally reduced my anxiety levels at work. However recently I&apos;ve come into a place of work where JavaScript is written and often maintained by software engineers for whom JavaScript isn&apos;t their speciality. Not wanting to go back to the wild west of plain JavaScript, I adopted JSDoc. Here&apos;s how it went.</description><pubDate>Thu, 25 May 2023 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;I&apos;ve become an avid fan of &lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt; in recent years as it&apos;s improved my output, tightened up my appreciation for JavaScript&apos;s type coercion, and generally reduced my anxiety levels at work. However recently I&apos;ve come into a place of work where JavaScript is written and often maintained by software engineers for whom JavaScript isn&apos;t their specialty. Not wanting to go back to the wild west of plain JavaScript, I adopted &lt;a href=&quot;https://jsdoc.app/&quot;&gt;JSDoc&lt;/a&gt;. Here&apos;s how it went.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; I&apos;ve written a &lt;a href=&quot;/posts/better-type-safety-with-jsdoc&quot;&gt;follow up article&lt;/a&gt; about how JSDoc can result in better type safety than TypeScript.&lt;/p&gt;
&lt;p&gt;I remember going through projects a few years ago and taking out all of the JSDoc notations. We were switching to TypeScript and JSDoc was seen as old and no longer needed. However it&apos;s gained a resurgence in recent months, most notably because &lt;a href=&quot;https://github.com/sveltejs/kit/discussions/4429#discussioncomment-2423814&quot;&gt;Svelte is switching back to JSDoc from TS&lt;/a&gt;. But it has other benefits, and disadvantages, compared to TypeScript. Here&apos;s a few of the ones I&apos;ve noticed.&lt;/p&gt;
&lt;p&gt;Heads up! If you&apos;re looking for more info about this subject, there&apos;s an excellent &lt;a href=&quot;https://syntax.fm/show/624/is-jsdoc-better-than-typescript&quot;&gt;syntax.fm podcast episode&lt;/a&gt; about it, which covers more ground than I have here.&lt;/p&gt;
&lt;h2&gt;Benefits of JSDoc compared to TypeScript&lt;/h2&gt;
&lt;p&gt;TypeScript has always been my favourite approach to typing JavaScript, but it&apos;s not all positive. Here&apos;s some gotchas that I encounter and which still are sticking points with the language.&lt;/p&gt;
&lt;h3&gt;1. No compile step&lt;/h3&gt;
&lt;p&gt;This is one of the most cited reasons for not using TS. TypeScript doesn&apos;t run in any browser, so each time you&apos;re writing TS, you&apos;re having to take that out of your built JS again at run time. Not only does this mean that fancy typed code could still be wrong, you&apos;ve also got the overhead of compilation, and the extra tooling that goes with that.&lt;/p&gt;
&lt;p&gt;For my team most of our JS is collocated in large applications built predominantly with PHP. Having an extra compile step for JS as well as building the PHP application might be frustrating and lead to overhead in pipelines.&lt;/p&gt;
&lt;h3&gt;2. Easier to follow&lt;/h3&gt;
&lt;p&gt;This is the main reason my team were not up for switching at the moment. Although they&apos;re pretty good at JavaScript overall, they&apos;re not used to newer ECMAScript syntax, so when they are working on a project without my involvement, they will struggle to understand what&apos;s going on and it could slow them down significantly. Training is the  solution here, but the business (and the engineers themselves) have to see the real benefit to it first. At the moment, that benefit isn&apos;t clear to them, and it&apos;s likely that other teams with engineers who have backgrounds in different languages and tools won&apos;t see it either.&lt;/p&gt;
&lt;p&gt;JSDoc is a totally different syntax but it is feature-rich, especially when compared to TypeScript, for instance you can even specify what error is thrown by a function, something TypeScript cannot yet do.&lt;/p&gt;
&lt;h2&gt;Disadvantages of JSDoc compared to TypeScript&lt;/h2&gt;
&lt;p&gt;To be honest, since using it seriously, I&apos;ve been impressed with how far I&apos;ve been able to get with JSDoc. I have been able to &lt;a href=&quot;https://stackoverflow.com/questions/49836644/how-to-import-a-typedef-from-one-file-to-another-in-jsdoc-using-node-js#answer-52847569&quot;&gt;import type dictionaries&lt;/a&gt; and found out how to use &lt;a href=&quot;https://dev.to/sumansarkar/how-to-use-jsdoc-annotations-with-vscode-for-intellisense-7co&quot;&gt;VSCode Intellisense&lt;/a&gt; to get type hinting. But there have still been some caveats.&lt;/p&gt;
&lt;h3&gt;1. It&apos;s not as flexible&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; It &lt;em&gt;is&lt;/em&gt; as flexible, I just didn&apos;t know enough when I wrote this ... please see the &quot;addendums&quot; section on how I solve these issues&lt;/p&gt;
&lt;p&gt;If I need to negate the &quot;object is possible undefined&quot; error that you&apos;ve probably seen a hundred times if you&apos;re doing DOM manipulation or data fetching:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const thing = document.querySelector(&apos;#my-element&apos;)
addStuff(thing); // Error: type undefined is not assignable to type Thing 
// or
const data = await fetch(&apos;https://my-url.dev&apos;);
return data.results // error: data is possibly undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In TS, you can either force or cast it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const thing = document.querySelector(&apos;#my-element&apos;)
addStuff(thing!); // although probably don&apos;t do this 
addStuff(thing as Thing) // still not great but maybe you don&apos;t want to cascade down to catch undefined and oh yes this is so contrived
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But in JSDoc I&apos;m forced to add a lengthier explanation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const thing = document.querySelector(&apos;#my-element&apos;)
addStuff(
// thing is definitely defined by this point, see line 120
// @ts-ignore-next-line
  thing
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&apos;t know maybe this is better than a sneaky hashbang. But it sometimes feels quite frustrating especially with a lot of them on multiple lines.&lt;/p&gt;
&lt;h3&gt;2. More shenanigans which would be easier with casting&lt;/h3&gt;
&lt;p&gt;OK, so this is quite similar but I found it super frustrating.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const thing = document.querySelector(&apos;#my-element&apos;);

thing.style.display = &apos;block&apos;; // Error: property style is not available on type Element
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The querySelector method annoyingly returns an Element type, not HTMLElement, so every time I need to set styles via JavaScript (although it doesn&apos;t happen often), I have to do a ts-ignore-next-line. Again another situation where casting would have come in really handy.&lt;/p&gt;
&lt;h2&gt;How?&lt;/h2&gt;
&lt;p&gt;If you&apos;re looking for an explanation of how to implement this in your project, head over to Jared White&apos;s That HTML Blog website, &lt;a href=&quot;https://thathtml.blog/2025/12/nuances-of-typing-with-jsdoc/&quot;&gt;his article called &quot;The Nuances of JavaScript Typing using JSDoc&quot; explains his method for how to use TypeScript with JSDoc&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;All in all, I&apos;m super happy JSDoc exists. Without it, I would be getting super frustrated because there would be no types - and therefore no documentation - for our codebases. However, I&apos;ll be even happier if we can make the switch to TypeScript syntax one day.&lt;/p&gt;
&lt;h3&gt;Addendum&lt;/h3&gt;
&lt;p&gt;Since writing this article, I have found a few tools that have improved the feedback I&apos;m getting from the TypeScript compiler: instead of relying on my IDE (which caused issues when other team members were using a different IDE), I&apos;ve since implemented &lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-jsdoc&quot;&gt;eslint-plugin-jsdoc&lt;/a&gt;, which as you might imagine, has much better standardised linting and ensures that any errors or warnings aren&apos;t left up to the IDE, because both WebStorm and VS Code report different errors and sometimes miss things natively.&quot;&lt;/p&gt;
&lt;h3&gt;Addendum 2&lt;/h3&gt;
&lt;p&gt;As things have progressed, I have learned how you can override types for document.querySelector():&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/** @type {HTMLElement | null} */
const thing = document.querySelector(&apos;#my-element&apos;);

thing.style.display = &apos;block&apos;; // no error :tada:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only thing I&apos;m having real trouble with is React&apos;s &lt;code&gt;setState&lt;/code&gt; hook. I&apos;m hoping to have a write up for that ... once I solve it.&lt;/p&gt;
&lt;h3&gt;Follow up article&lt;/h3&gt;
&lt;p&gt;This article is overwhelmingly the most popular article on my blog, so I&apos;ll keep writing more articles as I solve different issues.&lt;/p&gt;
&lt;p&gt;I&apos;ve written a &lt;a href=&quot;/posts/better-type-safety-with-jsdoc&quot;&gt;follow up article&lt;/a&gt; which includes a section about typing events with JSDoc.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Is JSDoc Better Than TypeScript? A Real World Example&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Better Performance with the React Context API</title><link>https://deliciousreverie.co.uk/posts/using-context/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-context/</guid><description>Build for complexity and performance in this deep dive into React&apos;s Context API. This article was originally published in Net Magazine. </description><pubDate>Mon, 08 Apr 2019 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Monday, 8 April 2019 , originally at &lt;a href=&quot;https://www.creativebloq.com&quot;&gt;https://www.creativebloq.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;People are demanding more and more out of their online services, and businesses strive to excel these demands, leading to more and more complex sites ... and larger JavaScript bundles too!&lt;/p&gt;
&lt;p&gt;How can we continue to deliver a first-class experience for users whilst still building for these levels of complexity? One good way of doing this - whilst at the same time as delivering more performant apps - is by using React&apos;s Context API.&lt;/p&gt;
&lt;p&gt;This article was first published in Net Magazine. However, the Context API has been updated since. Please visit the React docs for a more up to date version of how to use Context.&lt;/p&gt;
&lt;h2&gt;What is Context and Why Use It? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#what-is-context-and-why-use-it&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sharing data and other properties across multiple React components previously required either an additional library (popular ones include Redux or Apollo GraphQL) or a lot of &quot;prop drilling&quot;. &quot;Prop drilling&quot; means passing data up and down multiple layers of components which could be labourious, difficult to change when new requirements are built, and lead to confusing code. The Context API, released in React 16.3, aims to reduce our dependency on prop drilling and external libraries.&lt;/p&gt;
&lt;p&gt;Apps can perform faster because changes made to properties no longer require a huge amount of re-rendering on a parent component, since the changes can be made in a separate component and handed down to child components from there.&lt;/p&gt;
&lt;h2&gt;How to Use Context &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#how-to-use-context&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have set up a demo of Context in use on Code Sandbox: &lt;a href=&quot;https://codesandbox.io/s/0pl62xq030&quot;&gt;https://codesandbox.io/s/0pl62xq030&lt;/a&gt;. Let&apos;s walk through this code.&lt;/p&gt;
&lt;p&gt;Context consists of 2 components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &quot;Provider&quot;&lt;/li&gt;
&lt;li&gt;A &quot;Consumer&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The easiest way I can think of explaining this relationship is that the &quot;Consumer&quot; is what will be using features &quot;Provided&quot; by our Context.&lt;/p&gt;
&lt;h3&gt;Initial Setup &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#initial-setup&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Create a new module called &quot;Context.js&quot; adding the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;;

export const MyContext = React.createContext();
export const MyContextConsumer = MyContext.Consumer;

export default class myContextProvider extends React.Component {
  state = {
    aRangeofOptions: [&quot;one&quot;, &quot;two&quot;, &quot;three&quot;],
    updatesOnClick: null,
    onItemSelect: item =&amp;gt; {
      this.setState({
        updatesOnClick: item
      });
    }
  };
  render() {
    const { children } = this.props;
    return (
      &amp;lt;MyContext.Provider
        value={{
          state: this.state
        }}
      &amp;gt;
        {children}
      &amp;lt;/MyContext.Provider&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might be able to see that on lines 2 &amp;amp; 3, we&apos;re calling on our core library, React, not some external dependency. That&apos;s the first benefit already!&lt;/p&gt;
&lt;p&gt;I like to set up the Consumer in this file, even though we don&apos;t use it here. It helps me keep track of the logic when it&apos;s all in one place.&lt;/p&gt;
&lt;p&gt;Inside our class I&apos;ve set up a few items in state to demonstrate what&apos;s going on here.&lt;/p&gt;
&lt;p&gt;Next, create a module containing a few items the user can interact with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;;
import { MyContextConsumer } from &quot;./Context&quot;;

export default class Module1 extends React.Component {
  render() {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;h2&amp;gt;Module One&amp;lt;/h2&amp;gt;
        &amp;lt;MyContextConsumer&amp;gt;
          {context =&amp;gt; (
            &amp;lt;div&amp;gt;
              {context.state.aRangeofOptions.map((item, index) =&amp;gt; (
                &amp;lt;&amp;gt;
                  {item}
                  &amp;lt;input
                    type=&quot;radio&quot;
                    name=&quot;selection&quot;
                    key={item.Optionvalue}
                    onClick={() =&amp;gt; context.state.onItemSelect(item)}
                  /&amp;gt;
                &amp;lt;/&amp;gt;
              ))}
              &amp;lt;div style={{ margin: &quot;20px 0&quot; }}&amp;gt;
                selected: {context.state.updatesOnClick || &quot;none&quot;}{&quot; &quot;}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
          )}
        &amp;lt;/MyContextConsumer&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this component, we can loop over an array in our context.state to create a few checkboxes, and a text description of the output.&lt;/p&gt;
&lt;p&gt;In the next module we&apos;re basically going to duplicate this code but leave out the loop. This module is so we can demonstrate how context can be passed down.&lt;/p&gt;
&lt;p&gt;Next we can create our main app.js file, or index.js if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import MyContextProvider from &quot;./Context&quot;;
import Module1 from &quot;./Module1&quot;;
import Module2 from &quot;./Module2&quot;;
import &quot;./styles.css&quot;;

function App() {
  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;MyContextProvider&amp;gt;
        &amp;lt;h1&amp;gt;Hello Context&amp;lt;/h1&amp;gt;
        &amp;lt;Module1 /&amp;gt;
        &amp;lt;hr /&amp;gt;
        &amp;lt;p&amp;gt;It&apos;s magic!&amp;lt;/p&amp;gt;
        &amp;lt;hr /&amp;gt;
        &amp;lt;Module2 /&amp;gt;
      &amp;lt;/MyContextProvider&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

const rootElement = document.getElementById(&quot;root&quot;);
ReactDOM.render(&amp;lt;App /&amp;gt;, rootElement);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the two modules we&apos;ve created don&apos;t have any props passed down from the parent. Instead you get this functionality with the Context provider and children inside that.&lt;/p&gt;
&lt;p&gt;But wait! There&apos;s more we can do to boost performance here.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Analyse Performance with React Developer Tools &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#boxout:-analyse-performance-with-react-developer-tools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;React Developer Tools, an extension available on both Firefox and Chrome browsers, is a tool provided by Facebook to help debug your React apps.&lt;/p&gt;
&lt;p&gt;It can also be pretty helpful at identifying where re-renders are happening in your app, allowing you the opportunity to refactor the code if you think it&apos;s necessary.&lt;/p&gt;
&lt;p&gt;To find out where re-renders are occuring in your app, open the developer tools. Along the top of the pane you should see a number of options, including &quot;Elements&quot;, &quot;console&quot; and other tools you&apos;ve probably used before. Once you&apos;ve installed the React Developer Tools you should see a new item labeled &quot;React&quot; Near the far end of this list.&lt;/p&gt;
&lt;p&gt;If you click on the cog icon, you&apos;ll see some additional settings, including &quot;Hilight Updates&quot;.&lt;/p&gt;
&lt;p&gt;Try interacting with your site when &quot;Hilight Updates&quot; is ticked. You might be surprised at how much re-rendering is happening, slowing down the experience for users and having a negative impact on your conversions.&lt;/p&gt;
&lt;p&gt;Now you know where to start fixing the problem!&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Reducing Re-rendering &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#boxout:-reducing-re-rendering&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First of all, let&apos;s explain that term. Re-rensweing is what happens when the component you&apos;re using is updated. This has performance implications especially for interactions like dragging (think sliders), and inputting text. Without proper care users could notice lagginess and be put off from using your site. And the potential for laggy or unresponsiveness increases with complexity (because more re-rendering will happen).&lt;/p&gt;
&lt;p&gt;I have found this to be especially true on devices that aren&apos;t the most expensive on the market. JavaScript parsing times can increase horiffically on low- and mid-range devices that don&apos;t run recent versions of Chrome or Firefox. A huge group of people could be impacted, severely affecting your conversions.&lt;/p&gt;
&lt;p&gt;One way I&apos;ve been able to reduce re-renders if by using my context&apos;s state to hold functions as well.&lt;/p&gt;
&lt;p&gt;By moving onItemSelect out of the class and into the state object, we can call it in a similar way (context.onItemSelect becomes context.state.onItemSelect in our modules), and avoid a component update.&lt;/p&gt;
&lt;h2&gt;Try Using Context Today &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#try-using-context-today&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Have you found yourself writing props={props} all over the place, or using the spread operator to pass props down anonymously (like this: &lt;code&gt;&amp;lt;MyModule {...props}/&amp;gt;&lt;/code&gt;)? Then perhaps Context is a way out of the hole you could have drilled yourself into.&lt;/p&gt;
&lt;p&gt;If you&apos;re looking for a way to reduce the potential for complexity in a project, or reduce performance bottlenecks and you don&apos;t need the extra features that a dedicated state management tool or client-side GraphQL server would provide, chances are Context could equally be your tool for the job.&lt;/p&gt;
&lt;p&gt;Context isn&apos;t going to fit everyone&apos;s use case, nor was it meant to. But it is a very useful API with some practical features that you might find give your code a little more ... well, context.&lt;/p&gt;
&lt;p&gt;Check out the working code on CodeSandbox: &lt;a href=&quot;https://codesandbox.io/s/0pl62xq030&quot;&gt;https://codesandbox.io/s/0pl62xq030&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Resources &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#resources&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;You can learn more about how to use the Context API by using the following resources:&lt;/p&gt;
&lt;h2&gt;Official Documentation &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#official-documentation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The official React documentation is a great place to start especially if you want to understand new APIs like Context, because they&apos;re written so well and shows real world examples that are easy to follow.&lt;/p&gt;
&lt;h2&gt;Courses &amp;amp; Blog posts &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#courses-and-blog-posts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I used these docs to get started using Context, as well as Flavio Copes&apos; guide to the Context API (&lt;a href=&quot;https://flaviocopes.com/react-context-api/&quot;&gt;https://flaviocopes.com/react-context-api/&lt;/a&gt;) and Kent C Dodds&apos; course &quot;Advanced React Component Patterns&quot; on &lt;a href=&quot;http://egghead.io/&quot;&gt;egghead.io&lt;/a&gt; (&lt;a href=&quot;https://egghead.io/courses/advanced-react-component-patterns&quot;&gt;https://egghead.io/courses/advanced-react-component-patterns&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Those resources are great. But there&apos;s nothing like diving in and trying it for yourself. It&apos;ll be challenging to begin with but that&apos;s the best way to learn! I freely admit to getting frustrated, writing poor implementations, and having to start over many times before I understood Context well enough to use it in a project. But if you give up, you only have to come back to it one more time ... until you finally crack it!&lt;/p&gt;
&lt;p&gt;To do that, you could use two fantastic resources that have recently become available:&lt;/p&gt;
&lt;h2&gt;Codesandbox &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#codesandbox&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Codesandbox (&lt;a href=&quot;http://codesandbox.io/&quot;&gt;codesandbox.io&lt;/a&gt;) is an online editor for creating apps in many languages, with built-in support for React. It&apos;s like codesandbox but with support for javascript modules&lt;/p&gt;
&lt;h2&gt;Spectrum.chat &lt;a href=&quot;https://deliciousreverie.co.uk/posts/netmag-using-context/#spectrum.chat&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://spectrum.chat/&quot;&gt;Spectrum.chat&lt;/a&gt;, built by the creator of styled-components and recently acquired by GitHub, is rapidly becoming a lively community. I&apos;ve found it a great place to share ideas, ask for help and chat about the latest developments in JavaScript, React and other tools I use every day. Hope to see you there!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Better Performance with the React Context API&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using CSS Grid In Production Today</title><link>https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/</guid><description>For me, as well as a lot of others, CSS Grid is the most exciting thing to happen since CSS3 ... possibly even CSS2. But many fear using it in production. This post tackles one common use case where a Flexbox fallback provides support for Internet Explorer and Edge. </description><pubDate>Sun, 09 Jul 2017 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For me, as well as a lot of others, CSS Grid is the most exciting thing to happen since CSS3 ... possibly even CSS2. But many fear using it in production. This post tackles one common use case where a Flexbox fallback provides support for Internet Explorer and Edge.&lt;/p&gt;
&lt;p&gt;I&apos;m really, really excited about CSS Grid. This new technology, which allows developers to finally build a structured document layout natively without using a hack, was released in most major browsers in about March of this year.&lt;/p&gt;
&lt;p&gt;I&apos;ve been closely following the work of Jen Simmons and Rachel Andrew on this subject, and have attended other talks about the subject. But now I want to join the chorus and say with a definite certainty: yes, you can use CSS Grid in production today.&lt;/p&gt;
&lt;p&gt;I already have.&lt;/p&gt;
&lt;h2&gt;Why CSS Grid? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/#why-css-grid&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I imagine that many of us are quite happy with the tools we currently use to lay out our web pages. I too was pretty confident using either Bootstrap&apos;s grid, Susy, or something I custom-built, to lay out my document using floats.&lt;/p&gt;
&lt;p&gt;I don&apos;t need to say here what&apos;s already been said, and which you&apos;ve probably heard, about floats being a hack. Suffice to say, that this approach leads to increased maintenance cost over the lifetime of a project and contributes to poor performance of a website.&lt;/p&gt;
&lt;p&gt;But floats are also a hack, just like tables were before that. CSS Grid is the hero the web needs.&lt;/p&gt;
&lt;h2&gt;But ... It&apos;s Not Supported! &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/#but-...-it&apos;s-not-supported!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like Rachel Andrew said in her recent blog post &lt;a href=&quot;https://rachelandrew.co.uk/archives/2017/07/04/is-it-really-safe-to-start-using-css-grid-layout/&quot;&gt;(link here)&lt;/a&gt; around 70% of browsers have support for Grid. That&apos;s most of the web. You might want to check your own analytics to see what you need to support for your project, however I believe I have come up with a workable solution for when you need to support IE11.&lt;/p&gt;
&lt;h2&gt;The Scenario &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/#the-scenario&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I often find myself with this UI layout in my projects: 2 repeating columns of content, inside which are elements, usually designed with dummy content of the same height. This never works in production with floats. Sooner or later I get gaps like great big ugly missing teeth everywhere.&lt;/p&gt;
&lt;p&gt;The way I&apos;ve traditionally coped with this is very convoluted:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;row&quot;&amp;gt;
&amp;lt;?php $count=0; foreach ($items as $item) : ?&amp;gt;
&amp;lt;?php if ($count % 2 == 0 { ?&amp;gt;
&amp;lt;/div&amp;gt;&amp;lt;div class=&quot;row&quot;&amp;gt;
&amp;lt;?php } ?&amp;gt;
&amp;lt;?php $count++; endforeach; ?&amp;gt;
&amp;lt;/row&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Phew!&lt;/p&gt;
&lt;p&gt;This assumes that 1) Your frontend developer is authorised to, and competent at, writing server-side code, 2) you don&apos;t mond doing a lot of counting, 3) you don&apos;t mind spurious markup everywhere.&lt;/p&gt;
&lt;p&gt;This approach is messy, but it works.&lt;/p&gt;
&lt;h2&gt;Supporting 97% of the Web &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-css-grid-in-production-today/#supporting-97percent-of-the-web&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&apos;s the alternative. I can support IE11, Edge, and older versions of some browsers by using flexbox instead of grid.&lt;/p&gt;
&lt;p&gt;First, I declare the flexbox layout:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@media(min-width: 768px) {
  .container {
    display: flex;
    flex-wrap: wrap;
    margin-left: -15px;
    margin-right: -15px;

    &amp;gt; * {
      flex: 1;
      flex-basis: 46%;
      margin: 15px;
    }
  }
}
@media(min-width: 1200px) {
  .container {

    &amp;gt; * {
      flex-basis: 47%;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please note: this is 22 lines of code that I hope I will be able to remove from the project at some point. If not, it isn&apos;t doing any harm where it&apos;s not used.&lt;/p&gt;
&lt;p&gt;Now, below that I use an @supports rule to scope the grid layouts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@supports (display: grid) {

  @media (min-width: 768px) {
    .container {
      display: grid;
      grid-template-columns: repeat(2, 50fr);
      grid-gap: 15px;

      &amp;gt; * {
        margin: 0;
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is only 11 lines of code (not counting the lack of a &amp;lt;div class=&quot;row&quot;&amp;gt; etc, and doesn&apos;t use hacks like negative margins. Much better.&lt;/p&gt;
&lt;p&gt;This works in 97% of browsers today, &lt;a href=&quot;https://caniuse.com/#search=flexbox&quot;&gt;even Internet Explorer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, in both layouts, you get 2 columns of content, each item on a row has the same height, and they can expand to the content that you put into them.&lt;/p&gt;
&lt;p&gt;One thing to note: I&apos;m not replacing my floated grid in this project. I&apos;m complementing them at this stage for when floats are inadequate.&lt;/p&gt;
&lt;p&gt;But in the near future, I&apos;m really looking forward to swapping out this approach for more semantic, web-native code.&lt;/p&gt;
&lt;p&gt;I hope this little demonstratons helps you to see how CSS grid works, is faster, less costly to maintain, and great for the web.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using CSS Grid In Production Today&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using a moka pot on an induction hob</title><link>https://deliciousreverie.co.uk/posts/using-moka-pot-on-an-induction-hob/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-moka-pot-on-an-induction-hob/</guid><description>This is a trick that wasn&apos;t on any of the articles I found using the old search engines. So I&apos;m posting it here. It&apos;s about how you can use an aluminium coffee moka pot on an induction hob.</description><pubDate>Wed, 29 May 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;This is a little trick that wasn&apos;t on any of the articles I found using the old search engines. Although those articles did contain a lot of ads .. if you, y&apos;know ... like ads. So I&apos;m posting it here. It&apos;s about how you can use an aluminium coffee mocha on an induction hob.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I went to Southern Italy a few weeks ago. It was great, thank you for asking. We stayed with a local family so enjoyed the best of the area and the cuisine.&lt;/p&gt;
&lt;p&gt;Our friends have one of those induction hobs, which I love by the way. They&apos;re very easy to keep clean. They also have an moka pot, one of those little silver things that you see on gas stoves often.&lt;/p&gt;
&lt;p&gt;Trouble is the pots are usually aluminium, which won&apos;t work on induction hobs. Don&apos;t get technical on me, I have no idea why.&lt;/p&gt;
&lt;p&gt;It turns out what they&apos;d done is &lt;em&gt;put the moka pot inside a frying pan&lt;/em&gt; on the oven. Hah. Simple as that. The frying pan can transfer the heat, and the moka pot heats up beautifully.&lt;/p&gt;
&lt;p&gt;So now you know. By the way, this could be a bad idea for some reason I haven&apos;t thought of, so just to be clear: try this at your own risk!&lt;/p&gt;
&lt;h2&gt;How to make great coffee&lt;/h2&gt;
&lt;p&gt;Let me just share this with you because if you care enough about coffee to get a moka pot, you probably would like to know how my Italian friends made us cuppacinos every morning.&lt;/p&gt;
&lt;p&gt;First thing to do is put the moka pot on. You can cheat a bit and add boiling water to the pot. It&apos;s much faster but you have to mind you don&apos;t burn your fingers when you screw the pot back together.&lt;/p&gt;
&lt;p&gt;After putting the moka pot on, they heated up half a cup of milk. Then poured that into a french press and plunged the milk inside the press for around 30 seconds. This turned the milk into a lovely frothy texture.&lt;/p&gt;
&lt;p&gt;Once the moka was done, they poured the coffee into a cup with 2 teaspoons of sugar and mixed it together vigorously until all the sugar was absorbed.&lt;/p&gt;
&lt;p&gt;The final step was to pour the frothy milk into a cup then add the sugary coffee.&lt;/p&gt;
&lt;p&gt;I&apos;m sorry. You&apos;re likely going to start resenting the £4 or more those places charge for coffee once you&apos;ve tried this!&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using a moka pot on an induction hob&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using Pop!_OS for Work</title><link>https://deliciousreverie.co.uk/posts/using-popos-for-work/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-popos-for-work/</guid><description>In November last year I switched from using Windows at my place of work to using Pop!_OS by System76, an Ubuntu derivative designed for developers, researchers and scientists. What motivated me to make the switch? What benefits, and disadvantages, has it brought me? </description><pubDate>Sat, 10 Feb 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In November last year I switched from using Windows at my place of work to using Pop!_OS by System76, an Ubuntu derivative designed for developers, researchers and scientists. What motivated me to make the switch? What benefits, and disadvantages, has it brought me?&lt;/p&gt;
&lt;p&gt;When I came to work at my current employer, I got given a PC to develop on. I was eager to fit in and please everyone, and accepted the situation with the single caveat that it had to run WSL (Windows Subsystem for Linux, commonly known as Linux on Windows).&lt;/p&gt;
&lt;h2&gt;Using Windows Subsystem for Linux &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#using-windows-subsystem-for-linux&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I&apos;ve enjoyed using WSL, and it certainly has made Windows much more painless to use as a developer. I spend a lot of my time in the terminal day-to-day, using various web development tools including git, node, yarn, and other tools such as WPScan to analyse legacy sites. But it does have certain limitations. For example, I could never get Mysql to work on the command line. Only Atom allowed me to open files straight into my text editor. WSL is a brilliant innovation and I hope the team continues to push the boundaries there.&lt;/p&gt;
&lt;p&gt;But in the end, it wasn&apos;t quite enough for me. I also never got embedded into the Windows workflow. You know, little things like how windows layer on top of one another instead of occupying their own spaces. I never got over little things like that which interrupted my mental model of my workspace.&lt;/p&gt;
&lt;h2&gt;Why Not MacOS? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#why-not-macos&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For a loooong time I&apos;ve been a Macintosh afficionado, ever since my friend Martin Harries sold me a Performa in the early 90s. Although the OS is a great solution for a lot of individuals, and I can&apos;t argue that it&apos;s visually appealing.&lt;/p&gt;
&lt;p&gt;But when you start getting under the hood you come up against its inadequacies.&lt;/p&gt;
&lt;p&gt;I didn&apos;t like the absence of a package manager to maintain the OS from the command line. Also, a lot of the apps are a layer on top of command line programs which interfaces I now prefer for a lot of tasks.&lt;/p&gt;
&lt;h2&gt;Enter Pop!_OS &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#enter-pop!_os&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pop!_OS is billed as a system built for creators. It&apos;s interface is clean and out-of-the way, although it reflects well the 70s design ethos of its creators, System 76.&lt;/p&gt;
&lt;p&gt;Where Pop! came into its own for me was it&apos;s inbuilt terminal. I started off downloading and configuring Hyper, my go-to choice on the Mac. But after a while this broke, leading me to use the native terminal. It didn&apos;t take me long to figure out this had already been configured really well with a lot of the extensions I would&apos;ve added anyway.&lt;/p&gt;
&lt;p&gt;The other great thing is the Super key. On Windows, the Super key will allow you to search your programs. On Mac, the super key with a modifier gives you either a search / app launcher, or it tabs through your open apps.&lt;/p&gt;
&lt;p&gt;But on Ubuntu (of which Pop is a derivative), it serves both as an app launcher and zooms out all of your open apps, allowing you to visually switch between everything, or start a new workflow. It also gives you access to your desktop spaces so you can see other workflows in progress.&lt;/p&gt;
&lt;p&gt;For my mental model this is absolute genius. I&apos;ve got a pretty visual mind so seeing all my work spaces tiled out like this is really helpful in giving me some context.&lt;/p&gt;
&lt;h2&gt;Caveats, or How Not to Break Your Computer &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#caveats-or-how-not-to-break-your-computer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For many people, using Windows or MacOS is insurance ... you know you&apos;re probably not going to be able to break the OS irrevocably, and if you do you can just restore it from the cloud. Ubuntu can have this kind of setup, however I ... haven&apos;t configured it. I have broken things a few times, mostly by revoking my own permissions to edit files or folders.&lt;/p&gt;
&lt;p&gt;Hah, yeah, I&apos;m such a newbie at this.&lt;/p&gt;
&lt;p&gt;But on the other hand, I have always found a solution from the excellent community support forums, or helpful FOSS software maintainers.&lt;/p&gt;
&lt;p&gt;Whilst there&apos;s cause for concern, I&apos;m not worried to the point of paralysis. It&apos;s a bit like doing DIY. Yes, you can break stuff, but you have to try your best, and call in the experts when you get stuck.&lt;/p&gt;
&lt;p&gt;One day, in the far distant future, I hope I can help others who might be in a similar position.&lt;/p&gt;
&lt;h2&gt;App Availability &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#app-availability&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One thing that grates against the argument that you do need a bit of familiarity to aid your workflow is that a lot of the apps I&apos;m used to using aren&apos;t available for Linux.&lt;/p&gt;
&lt;p&gt;A good example is Sketch, which was my go-to design tool.&lt;/p&gt;
&lt;p&gt;I&apos;ve found a great replacement in &lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt;, which is superbly responsive on a browser if it doesn&apos;t currently have a native app. I&apos;m also really looking forward to Alecaddd&apos;s &lt;a href=&quot;https://github.com/Alecaddd/akira&quot;&gt;Akira&lt;/a&gt;, which is in beta but looks really really promising.&lt;/p&gt;
&lt;p&gt;For raster graphics editing, I have used GIMP. As much as people rave about it, I have to say ... I hate it. It&apos;s just so counterintuitive. Instead, I&apos;ve been using &lt;a href=&quot;https://www.photopea.com/&quot;&gt;Photopea&lt;/a&gt;, which opens just about any graphic format, and is a pretty decent editor in the style of Photoshop.&lt;/p&gt;
&lt;p&gt;These tools have meant I hardly skip a beat when I am handed a set of design files.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-popos-for-work/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pop!_OS is a smart, well designed release of Ubuntu which has sped up my workflow massively. I thoroughly enjoy using it. I&apos;m seriously considering replacing my home laptop MacOS system in favour of it. The only thing that&apos;s holding me back is the thought that I might lose the ability to play the platform game Braid. That would be very sad, but not essential!&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using Pop!_OS for Work&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using the Srcset attribute on images</title><link>https://deliciousreverie.co.uk/posts/using-the-srcset-attribute-on-images/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-the-srcset-attribute-on-images/</guid><description>So, I&apos;ve been using the relatively new (and somewhat confusing) srcset attribute to serve a couple of interesting use cases lately. This spec is really, really useful in certain circumstances, two of which I&apos;d like to share with you here. </description><pubDate>Fri, 15 Jan 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;h1&gt;Using the Srcset attribute on images&lt;/h1&gt;
&lt;p&gt;Published on Friday, 15 January 2016&lt;/p&gt;
&lt;p&gt;So, I&apos;ve been using the relatively new (and somewhat confusing) srcset attribute to serve a couple of interesting use cases lately. This spec is really, really useful in certain circumstances, two of which I&apos;d like to share with you here.&lt;/p&gt;
&lt;h2&gt;Situation 1: You want to use SVGs, but need to support browsers that don&apos;t &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-the-srcset-attribute-on-images/#situation-1:-you-want-to-use-svgs-but-need-to-support-browsers-that-don&apos;t&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;SVG support is fairly advanced across the browser spectrum (above), but srcset is supported less, especially by legacy IE browsers (below). This allows us to use the srcset attribute to specify an SVG image (or WebP for that matter), allowing newer browsers to download the Srcset image in preference, and older browsers to use the fallback .png file (or whatever else).&lt;/p&gt;
&lt;p&gt;I can&apos;t claim to have thought of this myself, Sara Soueidan covered this concept &lt;a href=&quot;https://sarasoueidan.com/blog/svg-picture/&quot;&gt;on her blog in February 2015&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The markup for this could actually be quite minimal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;logo.png&quot;	srcset=&quot;logo.svg&quot;	alt=&quot;alt text goes here&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browsers that recognise srcset will download that asset, so you&apos;ll get a nice crisp SVG, otherwise older browsers (which don&apos;t have support for svg either) will get the image.&lt;/p&gt;
&lt;p&gt;I routinely add dimensions to my srcsets to avoid rendering problems in certain browsers where they will always render the SVG at the natural height &amp;amp; width of the SVG, so if you have a different sized png, things can get a little crazy.&lt;/p&gt;
&lt;h2&gt;Situation 2: You want to use 2 different images at different breakpoints&lt;/h2&gt;
&lt;p&gt;Just recently I had a client requirement for an image that would be wide &amp;amp; narrow at large breakpoints, and thinner and taller at smaller, like this image demonstrates:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Yes I could do this with background images in CSS, but then the browser would have to download both images, increasing the page weight. Srcset solves this problem by fetching the appropriate image based on what sizes you stipulate in your media queries. This took me a few tries to get right, but I ended up with a robust solution that would conserve bandwidth:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img
	src=&quot;fallback.png&quot;
	srcset=&quot;
			small.png 649w,
			large.png 1140w
		&quot;
	sizes=&quot;
		(max-width:767px) 649w,
		(min-width:768px) 1140w
	&quot;
       alt=&quot;descriptive text&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What I&apos;m doing is specifying that the browser should behave this way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If the viewport width is below this, show the smaller image&lt;/li&gt;
&lt;li&gt;If the viewport is above that, use the other image&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started off without the max-width query, but was always getting the larger image, which seemed to render correctly after I introduced this query.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ericportis.com/posts/2014/srcset-sizes/&quot;&gt;Eric Portis&apos; comprehensive explanation&lt;/a&gt; was great at getting me to think about how to do this, what I&apos;m trying to do here is break it down to be something I can use in production without having to fry my little brain at the staggering genius that are the foundation tools of our trade.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using the Srcset attribute on images&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using WordPress as a Headless CMS</title><link>https://deliciousreverie.co.uk/posts/using-wordpress-headless-cms/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-wordpress-headless-cms/</guid><description>This is the reproduction of an article I wrote for Net Magazine and was published in Issue 308 (August 2018).</description><pubDate>Wed, 25 Jul 2018 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In case you missed it, here&apos;s the article and links to the example code I wrote to help developers get started with Gatsbyjs.&lt;/p&gt;
&lt;p&gt;Code for this example: &lt;a href=&quot;https://github.com/endymion1818/netmag-wpheadless&quot;&gt;https://github.com/endymion1818/netmag-wpheadless&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;You Will Need &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#you-will-need&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;A computer with an internet connection (obvs)&lt;/li&gt;
&lt;li&gt;A code editor (Atom or VS Code)&lt;/li&gt;
&lt;li&gt;A command line shell / terminal&lt;/li&gt;
&lt;li&gt;A recent version of NodeJS installed (you can download &amp;amp; install from here: https://nodejs.org/en/)&lt;/li&gt;
&lt;li&gt;A WordPress site to get content from. If you don&apos;t have one, you can use the WordPress.com platform (wordpress.com) or this Heroku build pack: https://www.technomile.com/capabilities/application-development/heroku/wordpress&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Getting Started &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#getting-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Using WordPress&lt;/p&gt;
&lt;p&gt;WordPress comes out-of-the box with the REST API, and that is what we&apos;re going to use to query your data. So we don&apos;t really need anything else! Our display site is totally separate from our content site, so we won&apos;t need a theme or any other customisation outside a few (optional) plugins.&lt;/p&gt;
&lt;p&gt;The exception is if you need custom meta fields for extra content areas you&apos;re probably using Advanced Custom Fields to do so. You can add that data to the WordPress API buy installing this plugin: &lt;a href=&quot;https://en-gb.wordpress.org/plugins/acf-to-rest-api&quot;&gt;https://en-gb.wordpress.org/plugins/acf-to-rest-api&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Using a Static Site Generator&lt;/p&gt;
&lt;p&gt;Now that we have our content source, let&apos;s fetch the data and display it using a static site generator (see the box &quot;why use a static site generator&quot;). My weapon of choice in this realm is Gatsby (&lt;a href=&quot;https://gatsbyjs.org/&quot;&gt;https://gatsbyjs.org&lt;/a&gt;), an excellent static site generator built with JavaScript.&lt;/p&gt;
&lt;p&gt;If you&apos;re looking for a good way to build on your JavaScript skills, and learn React by getting stuck into some code, I highly recommend trying Gatsby to do so. I&apos;ve learned a lot myself by playing with it.&lt;/p&gt;
&lt;p&gt;First, let&apos;s install a command-line tool that allows us to create Gatsby sites:&lt;/p&gt;
&lt;p&gt;npm install -global gatsby-cli&lt;/p&gt;
&lt;p&gt;Now, navigate to the folder where you want to keep your site, and run this command:&lt;/p&gt;
&lt;p&gt;gatsby new blog&lt;/p&gt;
&lt;p&gt;This will create a new folder called &apos;blog&apos; and install Gatsby and its dependencies to this folder. Open this folder in your favourite text editor. There might seem to be a lot of files there. Don&apos;t worry, we&apos;ll only be directly editing the gatsby-config.js, gatsby-node.js files, and the src folder, which is where our templates live.&lt;/p&gt;
&lt;h3&gt;Why Headless? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#why-headless&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I first heard about this approach from a talk I watched from Twin Cities Drupal (&lt;a href=&quot;https://www.youtube.com/watch?v=GX9z5M9mz30&quot;&gt;https://www.youtube.com/watch?v=GX9z5M9mz30&lt;/a&gt;). I liked the idea of a seperation of concerns between authoring content and viewing that content.&lt;/p&gt;
&lt;p&gt;I had also already experienced how easily one server can go down, taking out all of the sites on it, leading to minutes or hours of heart-stopping panic (there were 24 hours on mine). I had also seen how a monolithic CMS-based site could suffer a security compromise and take a lot of effort to repair (that took me more than 2 days of unpaid work).&lt;/p&gt;
&lt;p&gt;I had also seen the benefits of CDNs (content delivery networks) that can store your image, audio and video files on servers optimized for fast delivery, and can duplicate those files across the world for speedy delivery to those regions if needed. What if your entire website could benefit from this approach? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/talk-how-were-using-wordpress-as-a-headless-cms&quot;&gt;I gave a talk at WordCamp London 2018 about this subject&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Getting our Content &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#getting-our-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step we want to take is to fetch our content from the WordPress site&apos;s API.&lt;/p&gt;
&lt;p&gt;To do that, we&apos;re going to install gatsby-source-wordpress, a prewritten plugin for WordPress. This is one of the main reasons I love Gatsby - you can get your data from many different sources. A lot of static site generators are restricted to using Markdown files, but Gatsby is very flexible.&lt;/p&gt;
&lt;p&gt;Gatsby&apos;s plugin ecosystem is very mature with loads of prewritten ways of getting your data, and lots of other functionality that comes in useful too.&lt;/p&gt;
&lt;p&gt;To install the plugin, first change directory into your new Gatsby site by using this command: cd blog.&lt;/p&gt;
&lt;p&gt;Now run this command: npm install --save gatsby-source-wordpress&lt;/p&gt;
&lt;p&gt;Now open the gatsby-config.js file. There will already be some basic setup Gatsby gives us by default. We&apos;re going to add to that to configure our plugin here.&lt;/p&gt;
&lt;p&gt;Did it work?&lt;/p&gt;
&lt;p&gt;You can check by opening your terminal, typing gatsby develop and watch what happens. Be warned! Even if you got your settings correct, You will get some warnings anyway, this may be Gatsby looking for content which you haven&apos;t written yet.&lt;/p&gt;
&lt;p&gt;If the last few lines looks something like this, you&apos;re probably OK:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You can now view gatsby-starter-default in the browser.

  https://localhost:8000/

View GraphiQL, an in-browser IDE, to explore your site&apos;s data and schema

  https://localhost:8000/___graphql

Note that the development build is not optimized.
To create a production build, use gatsby build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If that isn&apos;t what you&apos;re getting, check your WordPress site isn&apos;t on a subdomain, and that it&apos;s definitely using HTTPS or HTTP, and you have the same in your settings.&lt;/p&gt;
&lt;p&gt;Now we can go to &lt;a href=&quot;https://localhost:8000/&quot;&gt;https://localhost:8000/&lt;/a&gt; and see our Gatsby site!&lt;/p&gt;
&lt;p&gt;Can We Query our Data?&lt;/p&gt;
&lt;p&gt;You may have noticed that there is no WordPress content here. This is because we haven&apos;t told Gatsby what to do with it yet. Before we do that, let&apos;s just check we have actually got our content available to GatsbyTo do that, visit this url:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://localhost:8000/___graphql&quot;&gt;https://localhost:8000/___graphql&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This built-in tool is called GraphiQL, and is another secret power of Gatsby.&lt;/p&gt;
&lt;p&gt;GraphQL is similar to REST : it&apos;s a way to query data. But with GraphQL, you can interact with your data much more easily. GraphiQL (a visual IDE for GraphQL) can show us some of these tricks. On the left panel, try typing the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{  allWordpressPost {    edges {      node {        id        slug        status        template        format      }    }  }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might look a bit like JSON, but it&apos;s not. It&apos;s a new query language that I think one day will largely replace REST as a way of communicating with APIs.&lt;/p&gt;
&lt;p&gt;What did you get when you pressed CTRL + Enter in GraphiQL? You hopefully will have seen your WordPress posts on the right site of the screen, something like this:&lt;/p&gt;
&lt;p&gt;We are actually going to use this query in our next step, so keep it handy! You might want to see what other data you can get with GraphiQL whilst you&apos;re here. If you want to do that, try moving the cursor around and typing either CTRL + Space and / or CTRL + Enter. That will reveal other groups of content.&lt;/p&gt;
&lt;p&gt;So, we now have content in Gatsby. Next, we need to display it...&lt;/p&gt;
&lt;h2&gt;Displaying our Posts &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#displaying-our-posts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For this next step we&apos;re going to use the gatsby-node.js file.&lt;/p&gt;
&lt;p&gt;gatsby-node.js is a file you can use to interact with Gatsby&apos;s &quot;Node API&quot;. Here you can control how your site is generated and create pages, posts and more.&lt;/p&gt;
&lt;p&gt;We&apos;re going to write some instructions here to tell Gatsby what to do with our data:&lt;/p&gt;
&lt;p&gt;This code creates pages from our GraphQL query, and for each page it&apos;ll use a template we&apos;ve defined (/src/templates/post.js). So next, we need to create that file!&lt;/p&gt;
&lt;p&gt;Create post template&lt;/p&gt;
&lt;p&gt;Inside the /src/ folder, create a folder called templates, and a file inside that called post.js. Add to it this code:&lt;/p&gt;
&lt;p&gt;This uses a different GraphQL query to get data about the specific post it&apos;s been fed by the gatsby-node.js file, then uses React to render that out into the browser.&lt;/p&gt;
&lt;p&gt;If you want to quickly see a list of all your posts, you can type https://localhost:8000/a into your browser&apos;s address bar. This will take you to a development 404 page, which lists all of your posts. Click on one to visit it!&lt;/p&gt;
&lt;h2&gt;Next Steps &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#next-steps&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We&apos;ve scratched the surface of how to use WordPress as a headless CMS, and I hope I&apos;ve introduced you to some interesting concepts and tools that you might be able to use and experiment with in the future.&lt;/p&gt;
&lt;p&gt;There&apos;s a lot more to this story, and my colleagues and I have blogged about it extensively, and I&apos;ve also written more on my personal blog, Delicious Reverie (&lt;a href=&quot;https://deliciousreverie.co.uk/&quot;&gt;https://deliciousreverie.co.uk/&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Please, keep in touch with me via those channels and on Twitter to hear more exciting developments in the world of headless CMS!&lt;/p&gt;
&lt;h3&gt;Why use a Static Site Generator? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/published-article-using-wordpress-headless-cms/#why-use-a-static-site-generator&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Traditionally, when a user visits a site, some code where the website lives kicks into action, getting data from a database, inserting it into templates, and stitches page pieces together before sending that code down to the user. This can take quite a bit of time.&lt;/p&gt;
&lt;p&gt;A static site generator does all of this before the user gets to the website. Because of this they can be a lot faster, and a lot less prone to security risks (because there&apos;s no code running behind the scenes on the server). They allow us to have a website with dynamic content (blog posts, pages, products, or any other class of content) which might need extra pages for archives, categories, and a way of making &amp;amp; saving drafts.&lt;/p&gt;
&lt;p&gt;Static Sites can be hosted entirely on CDN services like Netlify (&lt;a href=&quot;https://www.netlify.com/&quot;&gt;https://www.netlify.com/&lt;/a&gt;) that have generous free plans and take away the worry of managing servers.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using WordPress as a Headless CMS&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Using WSL (BASH for Windows)</title><link>https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/</guid><description>At a new job I&apos;ve been tasked with moving to Windows as my main production environment. At first approaching this idea with some trepidation, I have since discovered and been able to install the new Windows Subsystem for Linux on my PC, and it&apos;s proven to be a really useful tool. </description><pubDate>Tue, 01 Nov 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Published on Tuesday, 1 November 2016&lt;/p&gt;
&lt;p&gt;At a new job I&apos;ve been tasked with moving to Windows as my main production environment. At first approaching this idea with some trepidation, I have since discovered and been able to install the new Windows Subsystem for Linux on my PC, and it&apos;s proven to be a really useful tool.&lt;/p&gt;
&lt;p&gt;At a new job I&apos;ve been tasked with moving to Windows as my main production environment. At first approaching this idea with some trepidation, I have since discovered and been able to install the new Windows Subsystem for Linux on my PC, and it&apos;s proven to be a really useful tool.&lt;/p&gt;
&lt;p&gt;&quot;Bash on Windows&quot; isn&apos;t really what it says it is. It&apos;s not simply the terminal, but linux commands for the whole of the windows subsystem. &lt;a href=&quot;https://msdn.microsoft.com/en-us/commandline/wsl/about&quot;&gt;There&apos;s a video here&lt;/a&gt; that demonstrates what&apos;s possible, but as a &quot;normal-ish&quot; developer with the sole aim of developing websites, and no special knowledge of Linux or Windows, I thought it would be interesting to share what I found to be most helpful, and most challenging, about using it.&lt;/p&gt;
&lt;h3&gt;The Problem &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#the-problem&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I was pleased that I was able to install WSL without a hitch &lt;a href=&quot;https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/&quot;&gt;using this guide on how-to geek&lt;/a&gt;, but that&apos;s where it stopped being easy for me.&lt;/p&gt;
&lt;p&gt;Problems arose because the build process my new employer had established required using a command-line php script which was a wrapper for &lt;a href=&quot;https://wp-cli.org/&quot;&gt;WP-CLI, the command-line interface for Wordpress&lt;/a&gt;. This script ran through a few steps that would be essential to me getting up and running and developing new sites:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Download Wordpress Core&lt;/li&gt;
&lt;li&gt;Add build plugins and remove existing dummy content&lt;/li&gt;
&lt;li&gt;Create a wp-config file&lt;/li&gt;
&lt;li&gt;Connect to MySQL, add and configure a database&lt;/li&gt;
&lt;li&gt;Download plugins and themes from a private Git repository&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As you can see, this required a number of connecting services, which were not already installed on either Windows, or WSL. I made the decision that I would install these first inside WSL, and see if that would be more useable. I figured that it would be more difficult to configure everything I needed using the more unfamiliar Windows Command line.&lt;/p&gt;
&lt;h3&gt;Command-line PHP and WP-CLI &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#command-line-php-and-wp-cli&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I found installing these two tools was fairly easy using wget, and adding to my BASH profile. The only hitch (that I discovered later) was that the php user didn&apos;t have the necessary permissions to create the wp-config.phpfile. I discovered that by changing ownership of the folder I was using for my web projects to www-data was the key.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ chown -R www-data /var/www/html/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;AMP (Apache, MySQL and PHP) &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#amp-(apache-mysql-and-php)&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This was the most difficult step out of all of them. I discovered quickly that my regular tools of choice (&lt;a href=&quot;https://www.mamp.info/&quot;&gt;MAMP&lt;/a&gt; or &lt;a href=&quot;https://www.wampserver.com/en/&quot;&gt;WAMP&lt;/a&gt;) meant that MYSQL was a Windows executable, and so not useable in bash. My heart sorta sank at this point. However, configuring my own AMP stack is something that I had wanted to do so ...&lt;/p&gt;
&lt;p&gt;I quickly found this excellent tutorial on &lt;a href=&quot;https://community.linuxmint.com/tutorial/view/486&quot;&gt;Linux Mint community forum&lt;/a&gt; which takes you step-by-step through nearly everything I needed to download and install the AMP stack.&lt;/p&gt;
&lt;p&gt;One major hitch that occurred was when I did get my website up and running, but all links on the site were 404ing. Turns out that I needed to configure MOD_REWRITE on Apache. &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/how-to-set-up-mod_rewrite-for-apache-on-ubuntu-14-04&quot;&gt;This tutorial on Digital Ocean&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com/questions/23665064/project-links-do-not-work-on-wamp-server&quot;&gt;this question on Stack Overflow&lt;/a&gt; helped immensely.&lt;/p&gt;
&lt;p&gt;Once I had gone through all this and changed a few things, I found that links still weren&apos;t working. Then, I noticed that Wordpress was telling me that it couldn&apos;t write to .htaccess, and gave me a snippet to paste there. Voilá, working websites.&lt;/p&gt;
&lt;h3&gt;GIT Access &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#git-access&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The final piece in this particular puzzle was GIT. I needed to set up global credentials and store them in the system memory so that the build script could use them when it required. I eventually discovered that you can use a credential helper to store your user and pass in memory:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ git config --global credential.helper cache
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once these were configured, I was able to run my company&apos;s build script, and all was well with the world.&lt;/p&gt;
&lt;h3&gt;Why I&apos;m Not Using It Now &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#why-i&apos;m-not-using-it-now&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So, after going through all this pain, I eventually did manage to get over my particular hurdle and configure my Windows environment to use our in-house script and WP-CLI, although it was a challenge.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;The principle reason was that there is a barrier between WSL and the Windows environment. It&apos;s not always perceptible, but it&apos;s there.&lt;/p&gt;
&lt;p&gt;The main thing is that the file system is still in flux. I found that the Linux filesystem &lt;a href=&quot;https://www.howtogeek.com/249966/how-to-install-and-use-the-linux-bash-shell-on-windows-10/&quot;&gt;aforementioned tutorial on How-To Geek&lt;/a&gt; has moved location in latest builds of WSL, and I couldn&apos;t locate it. This means that opening files in my text editor (Sublime Text or Atom) was an impossibility.&lt;/p&gt;
&lt;p&gt;Even if I had pursued it, I found out that there are &lt;a href=&quot;https://blogs.msdn.microsoft.com/wsl/2016/06/15/wsl-file-system-support/&quot;&gt;incompatibilities in the filesystem&lt;/a&gt; which could mean that files might not be compatible after opening. Some have reported that files become invisible in WSL after being opened using a Windows application.&lt;/p&gt;
&lt;p&gt;I did start using the venerable Vim to edit my files, and actually, it&apos;s something I want to go back to one day. But I&apos;m pretty sure in the short term at least, with looming project deadlines, it would frustrate me very much.&lt;/p&gt;
&lt;p&gt;There are also currently some issues with MySQL which the WSL team are looking into, and I did find it to be buggy. Hey, this is an early release, there&apos;s bound to be glitches that need ironing out.&lt;/p&gt;
&lt;h3&gt;What I Learned &lt;a href=&quot;https://deliciousreverie.co.uk/posts/using-wsl-bash-for-windows/#what-i-learned&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I still use WSL every day to navigate around my file system and to run build scripts, and that isn&apos;t going to change. For me, it&apos;s far more intuitive and I&apos;m comfortable with it.&lt;/p&gt;
&lt;p&gt;Although it&apos;s really nice to have it, but actually I don&apos;t think it&apos;s an essential. Being able to use BASH to open folders, or open folders in .exe apps, now that would be incredible.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Using WSL (BASH for Windows)&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Validate your commits with Git Hooks</title><link>https://deliciousreverie.co.uk/posts/validate-your-commits-with-git-hooks/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/validate-your-commits-with-git-hooks/</guid><description>With everything else we need to remember, checking formatting and linting is easy to forget. If this sounds like you, there are some great solutions out there. Here&apos;s how to use Git Hooks.</description><pubDate>Fri, 21 Feb 2025 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Many of us run linters (probably eslint) and formatters (like prettier) to make sure code is in a suitable state before merging. We do this so that it&apos;s easier for other people to modify the code we&apos;ve written. But have you, like me, gotten frustrated by failures in pipelines due to this seemingly small problems? It&apos;s comparatively costly for failures to happen there too, since the compute time in pipelines needs to paid for.&lt;/p&gt;
&lt;p&gt;With everything else we need to remember, checking formatting and linting is easy to forget. If this sounds like you, there are some great solutions out there.&lt;/p&gt;
&lt;h2&gt;Husky&lt;/h2&gt;
&lt;p&gt;Probably the most popular solution, &lt;a href=&quot;https://typicode.github.io/husky/&quot;&gt;Husky&lt;/a&gt;, can be installed on a per-project basis. It runs before you commit code to your project, modifying your changed files so that they are linted and formatted. Anything that&apos;s fixable will be modified and added to the commit.&lt;/p&gt;
&lt;p&gt;This is a simple, small, easy to use library that can be modified to suit your needs.&lt;/p&gt;
&lt;p&gt;But what if you can&apos;t use Husky, or don&apos;t want to?&lt;/p&gt;
&lt;h2&gt;Git Hooks&lt;/h2&gt;
&lt;p&gt;Another way to make sure you run linters is by using Git Hooks. I typically configure my global git so that each time I run &lt;code&gt;git commit&lt;/code&gt; my hook runs.&lt;/p&gt;
&lt;p&gt;Here are the steps I took to make it work.&lt;/p&gt;
&lt;h3&gt;1. Set up files and folders&lt;/h3&gt;
&lt;p&gt;Git recommends you use a folder called &lt;strong&gt;.git-templates&lt;/strong&gt;, and inside that a folder called &lt;strong&gt;hooks&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# from your Home folder
mkdir .git-templates
cd .git-templates
mkdir hooks
cd hooks
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now create a file called &lt;strong&gt;pre-commit&lt;/strong&gt;, no extension and make it executable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch pre-commit
chmod +x ./pre-commit
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Tell git about it&lt;/h3&gt;
&lt;p&gt;Next we need to tell git where to look for this hook. We can let it know by running the following&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git config --global init.templateDir &apos;~/.git-templates&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Git will look for the specific path (/hooks/pre-commit) that we set up and run the executable.&lt;/p&gt;
&lt;h3&gt;3. Set up the executable&lt;/h3&gt;
&lt;p&gt;Open the file &lt;strong&gt;pre-commit&lt;/strong&gt; in your favourite editor, and add the following&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# in the ~/.git-templates/hooks/pre-commit file
#!/bin/sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This line tells the executable which shell to use. I&apos;m using &lt;code&gt;sh&lt;/code&gt;, but you can use &lt;code&gt;bash&lt;/code&gt; or something else.&lt;/p&gt;
&lt;p&gt;Below that we can add the commands we want to run.&lt;/p&gt;
&lt;p&gt;Now this is where, because this is a global, it helps if you&apos;ve standardised your build / test/ format / lint commands. I&apos;ve done across many of my projects so I can add them to the file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm run lint:check
npm run format:check
npm run test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when I run &lt;code&gt;git commit&lt;/code&gt; in my project, I get the following message:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gc -m &apos;test&apos;

&amp;gt; myproject@1.0.0 lint:check
&amp;gt; eslint &apos;**/*.{js, ts}

&amp;gt; myproject@1.0.0 format:check
&amp;gt; prettier --check &apos;**/*.{js, ts}

checking formatting ...
[warn] src/my-file.js
[warn] Code style issues found in the above file. Run Prettier with --write to fix
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Git Hooks are great&lt;/h2&gt;
&lt;p&gt;What&apos;s even better is that if the project doesn&apos;t have any of these scripts, it&apos;ll fail silently and still allow you to commit as normal.&lt;/p&gt;
&lt;p&gt;I really love Husky, but I also am really happy to use a native solution that&apos;s built into Git, along with bash scripts, to make my coding workflow both cheaper and faster.&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Validate your commits with Git Hooks&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Waiting for an Element to appear in the DOM or shadow DOM with an Intersection Observer</title><link>https://deliciousreverie.co.uk/posts/waiting-for-an-element-with-intersection-observer/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/waiting-for-an-element-with-intersection-observer/</guid><description>An asynchronous solution which waits for elements to appear in the document. Covers both a normal Document and the Shadow DOM.</description><pubDate>Mon, 16 Dec 2024 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;&lt;strong&gt;An asynchronous solution which waits for elements to appear in the document. Covers both a normal Document and the Shadow DOM.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ve been working extensively with asynchronous HTML loading lately. Normally there are built-in APIs you can use to hook into when DOM elements are available. But a few times lately, my code has run outside of those APIs.&lt;/p&gt;
&lt;p&gt;So I came up with this implementation that&apos;s proved really useful for resolving elements that can appear asynchronously and even if they&apos;re inside the shadow DOM.&lt;/p&gt;
&lt;h2&gt;Using on a Drupal Project&lt;/h2&gt;
&lt;p&gt;If you&apos;re writing JavaScript for a Drupal project, youll notice that Drupal loads the DOM asynchronously. This is because it can have normal taxonomies and content, but also featured queues, which as I understand it, can get quite complex and need to be retrieved from the database. This can result in DOM elements being loaded out of order.&lt;/p&gt;
&lt;p&gt;If you&apos;re wanting to animate those elements, say for instance in a carousel, that&apos;s going to prove challenging.&lt;/p&gt;
&lt;p&gt;Drupal ships with it&apos;s own solution for this, &lt;code&gt;once()&lt;/code&gt;, which I understand it is originally &lt;a href=&quot;https://plugins.jquery.com/once/&quot;&gt;a jQuery thing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So if you&apos;re in Drupal-land, you might want to use that.&lt;/p&gt;
&lt;h2&gt;Any project&lt;/h2&gt;
&lt;p&gt;Although my project &lt;em&gt;was&lt;/em&gt; Drupal, my code existed outside of that ecosystem as a standalone project. Therefore I rolled this code.&lt;/p&gt;
&lt;p&gt;Let&apos;s walk through it.&lt;/p&gt;
&lt;h2&gt;Getting up and running&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;async function waitForElement(root, selector, timeout = 1000) {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The arguments in the function are &lt;code&gt;root&lt;/code&gt;, the root element. This is usually the &lt;code&gt;document&lt;/code&gt;, however as you&apos;ll see in the implementation there could be other situations where that&apos;s not true.&lt;/p&gt;
&lt;p&gt;For example, I can pass in a shadow Root, which is particularly handy for hooking into Web Components. I&apos;ve built a complex one as part of this application which I hope to blog about sometime.&lt;/p&gt;
&lt;p&gt;Then we have the &lt;code&gt;selector&lt;/code&gt;. This is the selector for the element you&apos;re waiting for as a string.&lt;/p&gt;
&lt;p&gt;Lastly, we have a &lt;code&gt;timeout&lt;/code&gt;. This sounds awful to start with but we don&apos;t want this thing running in the background forever, so the timeout of course means we can resolve after a reasonable amount of time has passed.&lt;/p&gt;
&lt;p&gt;This is hazardous I know. If we&apos;re on slower networks, it&apos;s going to take longer to load, meaning it could very easily timeout before the element appears.. what can I say, sometimes stuff fails.&lt;/p&gt;
&lt;h2&gt;Promises made to be broken&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;return new Promise((resolve, reject) =&amp;gt; {
  let element = root.querySelector(selector);
  if (element) {
    resolve(element);
    return;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re setting up a new &lt;code&gt;Promise&lt;/code&gt; so that we can &lt;code&gt;resolve&lt;/code&gt; it with the element if we need to, or &lt;code&gt;reject&lt;/code&gt; it if if fails. You could also instead return &lt;code&gt;null&lt;/code&gt; in the case of failures and handle it in the application.&lt;/p&gt;
&lt;p&gt;Next I check to see if the element is already there. It might have loaded already after all!&lt;/p&gt;
&lt;h2&gt;Observing mutations&lt;/h2&gt;
&lt;p&gt;Hmm now I sound like some DNA mad scientist. Promise this isn&apos;t going to get icky. Much.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const observer = new MutationObserver((mutations) =&amp;gt; {
      mutations.forEach((mutation) =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we&apos;re &lt;code&gt;new&lt;/code&gt;ing up a &lt;code&gt;MutationObserver&lt;/code&gt;. A mutation is triggered when anything in the DOM changes, and allows you to write a callback for when that happens.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (mutation.type !== &quot;childList&quot;) {
  return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because it fires on &lt;em&gt;every&lt;/em&gt; mutation we need to filter for the ones we&apos;re looking for. They can be &lt;em&gt;different types&lt;/em&gt; of mutation, but we&apos;re looking for a &lt;code&gt;childList&lt;/code&gt;, as opposed to changes in text or attributes of an element.&lt;/p&gt;
&lt;p&gt;Next we need to cycle through each of the &lt;code&gt;addedNodes&lt;/code&gt; to filter through them and discover if they might include our element. However, we also need to filter out a few more things:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mutation.addedNodes.forEach((node) =&amp;gt; {
  if (node.nodeType !== Node.ELEMENT_NODE) {
    return;
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Firstly, if it&apos;s an &lt;code&gt;ELEMENT_NODE&lt;/code&gt;, so a DOM element instead of a comment block (&lt;code&gt;&amp;lt;!--- this sort of thing ---&amp;gt;&lt;/code&gt;) or the &lt;code&gt;document&lt;/code&gt; itself, then we break out of the &lt;code&gt;forEach&lt;/code&gt; early, since we&apos;re only looking for those.&lt;/p&gt;
&lt;p&gt;However if it is we might have a warranted early success.&lt;/p&gt;
&lt;h2&gt;Early success&lt;/h2&gt;
&lt;p&gt;Let&apos;s be optimistic, if the first valid mutation &lt;em&gt;is&lt;/em&gt; the selector we&apos;re waiting for, then we have a win. However of course this might not be the first time our &lt;code&gt;oberver&lt;/code&gt; has been called, in which case we might need to cancel the timeout. It would also be sensible to disconnect the observer at that point.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (node.matches(selector)) {
  observer.disconnect();
  clearTimeout(timeoutId);
  resolve(node);
  return;
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Check if shadow root&lt;/h2&gt;
&lt;p&gt;If we haven&apos;t yet found the element we should first check if the node is in the shadow DOM, and whether we have it in this mutation:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (node.shadowRoot) {
  const shadowElement = node.shadowRoot.querySelector(selector);
  if (shadowElement) {
    observer.disconnect();
    clearTimeout(timeoutId);
    resolve(shadowElement);
    return;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Otherwise we should also set up an observer for the shadow root to see if it is going to appear there:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;observer.observe(node.shadowRoot, {
  childList: true,
  subtree: true,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let&apos;s check if the element we&apos;re looking for is within the root now that it&apos;s been updated. If so we can resolve that element and disconnect the observer.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;element = root.querySelector(selector);
if (element) {
  observer.disconnect();
  clearTimeout(timeoutId);
  resolve(element);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Listening for further changes&lt;/h2&gt;
&lt;p&gt;Now we have the substance of our observer we need to set it off by calling &lt;code&gt;observe&lt;/code&gt; which will keep watching the root element we supplied. That way any other changes will reveal whether the element we&apos;re looking for has arrived in the DOM:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;observer.observe(root, { childList: true, subtree: true });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my example we watch the &lt;code&gt;childList&lt;/code&gt; and the &lt;code&gt;subTree&lt;/code&gt; in case the element appears as a descendent of the parent and not a direct child. If you know that it&apos;s going to be a direct child of the element you don&apos;t need to supply these options.&lt;/p&gt;
&lt;h2&gt;Timeout&lt;/h2&gt;
&lt;p&gt;As we mentioned before, we don&apos;t want this to keep running indefinitely. At some point the DOM is going to either finish loading or we&apos;re going to have to stop waiting.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
timeoutId = setTimeout(() =&amp;gt; {
  observer.disconnect();
  reject(`Element not found: ${selector}`)
}, timeout);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way we disconnect the observer and we can either return and handle the error here or as I&apos;ve done pass it up the call stack with a &lt;code&gt;Promise.reject()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In this case you&apos;ll need to wrap a call to &lt;code&gt;waitForElement()&lt;/code&gt; with a &lt;code&gt;try {} catch {}&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;So the flow for this is that if the element isn&apos;t found immediately we&apos;ll call the &lt;code&gt;observer&lt;/code&gt;, then set a timeout to disconnect if it isn&apos;t ultimately found.&lt;/p&gt;
&lt;h2&gt;Happy Copypasta&lt;/h2&gt;
&lt;p&gt;Here&apos;s the full code which you can exploit to your heart&apos;s content.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Wait for an element to be added to the DOM or shadow DOM.
 * @param {ShadowRoot | Document} root - The root to observe.
 * @param {string} selector - The selector of the element to wait for.
 * @param {number} [timeout] - The time in milliseconds to wait before rejecting the promise.
 * @return {Promise&amp;lt;HTMLElement | Node&amp;gt;} - A promise that resolves with the element when it is added to the DOM or shadow DOM.
 */
function waitForElement(root, selector, timeout = 1000) {
  let timeoutId;
  return new Promise((resolve, reject) =&amp;gt; {
    let element = root.querySelector(selector);
    if (element) {
      resolve(element);
      return;
    }
    const observer = new MutationObserver((mutations) =&amp;gt; {
      mutations.forEach((mutation) =&amp;gt; {
        if (mutation.type !== &quot;childList&quot;) {
          return;
        }
        mutation.addedNodes.forEach((node) =&amp;gt; {
          if (node.nodeType !== Node.ELEMENT_NODE) {
            return;
          }
          if (node.matches(selector)) {
            observer.disconnect();
            clearTimeout(timeoutId);
            resolve(node);
            return;
          }
          const shadowElement = node?.shadowRoot?.querySelector(selector);

          if (shadowElement) {
            observer.disconnect();
            clearTimeout(timeoutId);
            resolve(shadowElement);
            return;
          }
          if (node.shadowRoot) {
            observer.observe(node.shadowRoot, {
              childList: true,
              subtree: true,
            });
          }
          element = root.querySelector(selector);
          if (element) {
            observer.disconnect();
            clearTimeout(timeoutId);
            resolve(element);
          }
        });
      });
    });

    observer.observe(root, { childList: true, subtree: true });

    timeoutId = setTimeout(() =&amp;gt; {
      observer.disconnect();
      reject(new Error(`Element not found: ${selector}`));
    }, timeout);
  });
}
export default waitForElement;

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

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Waiting for an Element to appear in the DOM or shadow DOM with an Intersection Observer&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>On website longevity</title><link>https://deliciousreverie.co.uk/posts/website-longevity/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/website-longevity/</guid><description>When you build something, how long do you expect it to last? Whilst it might be easy to think about the things we create as our legacy to the world, that might not be the case when it comes to our online creations ... </description><pubDate>Tue, 31 Mar 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;When you build something, how long do you expect it to last? Whilst it might be easy to think about the things we create as our legacy to the world, that might not be the case when it comes to our online creations ...&lt;/p&gt;
&lt;p&gt;When I was first experimenting with Gatsby, I built a site that I was really proud of. It was my first JAMStack project, and allowed me to publish content for a cause I wanted to support. However, only a few years later I found I could not continue to use it.&lt;/p&gt;
&lt;p&gt;What had happened was that things had moved on. I had built it with an early alpha of Gatsby v1, and since then the project has continued to iterate, being supported by it&apos;s vast online community.&lt;/p&gt;
&lt;p&gt;All of the dependencies that Gatsby uses under-the-hood had been updated too. React router had made way for reach router, there were new ways of handling many different things ... no doubt better ways, but ways that still left my little old website flaky after only about three years of uptime.&lt;/p&gt;
&lt;p&gt;This isn&apos;t necessarily a problem. People can still view the content I posted there in all those years that I was able to use it. And I could still have run it locally ... had I not wiped my computer and started over with a new install of Nodejs, Gatsby cli, and everything else.&lt;/p&gt;
&lt;p&gt;That last step seems to have rendered it challenging for me to even add a new locally-hosted Markdown blog page, and so I find I haven&apos;t updated the blog in quite a while now.&lt;/p&gt;
&lt;p&gt;I have three young kids ... if you have kids, you know that means practically zero time out. So there&apos;s no time for me to even plan - let alone carry out - a rebuild of the site.&lt;/p&gt;
&lt;p&gt;I guess what I&apos;m saying is that these new tools are awesome, and I don&apos;t want to stop using them. I just wish they didn&apos;t result in things being quite so ... ephermeral.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:On website longevity&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>What is Typescript?</title><link>https://deliciousreverie.co.uk/posts/what-is-typescript/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/what-is-typescript/</guid><description>What is TypeScript? How can it be useful to a person learning web development? I was asked this question on a Slack dicussion recently, and came up with the following example which seemed to be enough for my friends to grasp it. </description><pubDate>Thu, 14 May 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;What is TypeScript? How can it be useful to a person learning web development? I was asked this question on a Slack dicussion recently, and came up with the following example which seemed to be enough for my friends to grasp it.&lt;/p&gt;
&lt;p&gt;JavaScript is a dynamic language.Here’s an example using the browser’s console:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I assigned the var a to a number , the number 1, then I assigned the next var, b, to a string, &apos;two&apos; ... and then I added them together.&lt;/p&gt;
&lt;p&gt;The result was a string (1two) because JavaScript changed my number into a string. In a lot of other languages, you cannot do this. The compiler, what interprets your code, would throw an error.&lt;/p&gt;
&lt;p&gt;This example shows that JavaScript is a dynamic language, it changes types on the go (not just from strings to numbers but lots of others too) depending on a set of it’s own rules which sometimes don’t make sense. It’s a bit like the English language!&lt;/p&gt;
&lt;p&gt;TypeScript is a compiler which tries to stop JavaScript from doing this crazy stuff so it becomes more predictable.&lt;/p&gt;
&lt;h2&gt;When writing code &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-is-typescript/#when-writing-code&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;TypeScript can also be useful when using certain code editors.&lt;/p&gt;
&lt;p&gt;For example, I&apos;ve typed something that doesn&apos;t exist in the data structure here, and my compiler can warn me about it, meaning I don&apos;t have to see an error in the browser, then switch back to my code editor:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I can also get useful tools like this, so I have confidence even when I&apos;m typing, that my code is going to be correct.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;These two features are great timesavers, and claw back a lot of time you might have spent setting up TypeScript and debugging issues you might have as a result.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:What is Typescript?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Why Astro matters</title><link>https://deliciousreverie.co.uk/posts/why-astro-matters/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/why-astro-matters/</guid><description>Next, Nuxt, Gatsby, SvelteKit ... there&apos;s been an explosion of frontend application frameworks lately. It&apos;s never been a more delightful experience to spin up a new project. What&apos;s the point of difference with this one? Why does it &apos;matter&apos; so much? </description><pubDate>Tue, 20 Jul 2021 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Next, Nuxt, Gatsby, SvelteKit ... there&apos;s been an explosion of frontend application frameworks lately. I&apos;ve tried many (but not all) of them, and I&apos;ve got to say, it&apos;s never been a more delightful experience to spin up a new project. So much so, that I&apos;ve got hundreds of unfinished ones lying around everywhere.&lt;/p&gt;
&lt;p&gt;Recently, &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;, another new frontend application framework, launched itself on the unsuspecting JavaScript public.&lt;/p&gt;
&lt;p&gt;Whilst many of us may have been tempted to say &quot;oh no not another one&quot;, this framework really stood out to me.&lt;/p&gt;
&lt;p&gt;What&apos;s the point of difference with this one? Why does it &quot;matter&quot; so much? Well, consider this:&lt;/p&gt;
&lt;h2&gt;1. Frontend can be one happy family again &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-astro-matters/#1.-frontend-can-be-one-happy-family-again&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Astro could be considered the first frontend &quot;meta framework&quot;.&lt;/p&gt;
&lt;p&gt;What&apos;s one of those then? It&apos;s a &quot;set of core interfaces for common services and highly extensible backbone for integrating components &lt;a href=&quot;https://www.igi-global.com/chapter/java-web-application-frameworks/16864&quot;&gt;this is already Java thing by the way&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Astro is essentially a &quot;bring your own frontend&quot; approach to modern web frameworks. You can use whatever framework (oh, ok &quot;library&quot; then) you know and love, and still spin up a performant app that you can host almost anywhere.&lt;/p&gt;
&lt;p&gt;Think about the potential here. Astro could be the place the frontend finally comes together. It no longer matters (as much) what framework you use. Use them all if you like 🤷‍♂️.&lt;/p&gt;
&lt;p&gt;Love Vue? You can love Astro. React? Same. Svelte? You&apos;ll find no argument from Astro, because Astro is the glue that underpins how we build websites and applications.&lt;/p&gt;
&lt;p&gt;Great, innit? It&apos;ll probably never happen but I can dream, can&apos;t I?&lt;/p&gt;
&lt;h2&gt;2. Astro pushes the boundaries for every javascript framework* &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-astro-matters/#2.-astro-pushes-the-boundaries-for-every-javascript-framework*&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;(* oh, ok library then)&lt;/p&gt;
&lt;p&gt;Take a look at this tweet from Evan You, the creator of Vue:&lt;/p&gt;
&lt;p&gt;Is it a coincidence that Vue now can do a similar thing to Astro? did Astro get Evan to start thinking more about this problem? Could the same be said for the other frameworks too?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://deliciousreverie.co.uk/post/towards-better-rehydration/&quot;&gt;Better hydration is something I&apos;ve been wanting ever since the present generation of frontend application frameworks came out&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I know the React team have been working on it for a long time. &lt;a href=&quot;https://github.com/gatsbyjs/gatsby/issues/17993&quot;&gt;I even opened (very prematurely it turns out!) this issue on the GatsbyJS repo around 2 years ago&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;React 18&apos;s hydration prioritisation is a good step forward, however the whole DOM tree still need to be hydrated. Won&apos;t it be great when we need only attach JavaScript generated elements to the DOM when components really need them?!&lt;/p&gt;
&lt;p&gt;It would be wonderful to think that partial rehydration could be everywhere, it would certainly level the playing field and even things up a lot &lt;a href=&quot;https://gomakethings.com/progressive-enhancement-and-the-next-billion-web-users/&quot;&gt;for the next 1 billion web users&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Check out Astro &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-astro-matters/#check-out-astro&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you care about performance (you care right?) please check out this gamechanger. I&apos;m so excited for the potential here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://astro.build/&quot;&gt;https://astro.build&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Why Astro matters&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Why Atomic Design Makes Sense</title><link>https://deliciousreverie.co.uk/posts/why-atomic-design-makes-sense/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/why-atomic-design-makes-sense/</guid><description>Although I&apos;ve really gotten into the SASS revolution, I haven&apos;t yet adopted a method for structuring elements of CSS. I guess it&apos;s because none of them strike me as particularly visual, and being a former print designer, I tend to think in those terms. But something about Brad Frost&apos;s Atomic Design principles makes sense to me. Here&apos;s why.</description><pubDate>Mon, 23 Nov 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Although I&apos;ve really gotten into the SASS revolution, I haven&apos;t yet adopted a method for structuring elements of CSS. I guess it&apos;s because none of them strike me as particularly visual, and being a former print designer, I tend to think in those terms. But something about Brad Frost&apos;s Atomic Design principles makes sense to me. Here&apos;s why.&lt;/p&gt;
&lt;p&gt;When we&apos;re designing, we tend to think more about design principles instead of build principles. What I mean is that we think about rhythm, white space, alignment and these types of things. These are very necessary components of great web design as they are of design in other mediums.&lt;/p&gt;
&lt;p&gt;But the web is still a unique animal. If we&apos;re truly going to design for the web, we need to think about the medium itself, much as a printer considers the stock, the inks, and the situation artwork is going to be displayed in.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atomicdesign.bradfrost.com/&quot;&gt;https://atomicdesign.bradfrost.com&lt;/a&gt; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-atomic-design-makes-sense/#https:atomicdesign.bradfrost.com&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Atomic design really helps at this level: it helps us design repeatable elements that can be coded and re-used on different parts of our site. I don&apos;t intend to re-hash Atomic principles here, but take a look around, &lt;a href=&quot;https://blog.invisionapp.com/atomic-design-principles/&quot;&gt;perhaps at this post&lt;/a&gt;, or &lt;a href=&quot;https://atomicdesign.bradfrost.com/chapter-2/#atomic-design-is-for-user-interfaces&quot;&gt;read a pertinent excerpt from the book&lt;/a&gt;, to see what I mean.&lt;/p&gt;
&lt;p&gt;This is going to really help us when it comes to building things out in code, especially if it&apos;s someone else building it.&lt;/p&gt;
&lt;h2&gt;Thinking Like Developers &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-atomic-design-makes-sense/#thinking-like-developers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is important. I don&apos;t personally think it&apos;s a requirement for designers to be able to code — but I do believe we need to think like developers. If we comprehend the way a developer approaches a project, we&apos;ll see that system-based thinking (rather than individual page-based thinking) actually helps us get a product that reflects our design intentions more completely.&lt;/p&gt;
&lt;p&gt;And we&apos;ll also be able to empathise with our developers, which means greater understanding, greater cooperation and - again - much better result, not just in appearance but in cleaner, more efficient code that has performance benefits.&lt;/p&gt;
&lt;p&gt;And website performance is part of our job too, right?&lt;/p&gt;
&lt;h2&gt;Atomic Design as a Developer &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-atomic-design-makes-sense/#atomic-design-as-a-developer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As a developer, I&apos;m wondering if I use SASS partials to structure my code. Perhaps defining files this way:&lt;/p&gt;
&lt;p&gt;for atoms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a-type&lt;/li&gt;
&lt;li&gt;a-colors&lt;/li&gt;
&lt;li&gt;a-inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;for molecules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;m-card&lt;/li&gt;
&lt;li&gt;m-navbar&lt;/li&gt;
&lt;li&gt;m-lists&lt;/li&gt;
&lt;li&gt;m-formfields&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;for organisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;o-navbar&lt;/li&gt;
&lt;li&gt;o-contactform&lt;/li&gt;
&lt;li&gt;o-banner&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and so on..&lt;/p&gt;
&lt;p&gt;Hm, ok I&apos;m going to try this. Hope to give you a development update in due course.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Why Atomic Design Makes Sense&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Why &apos;Delicious Reverie&apos;?</title><link>https://deliciousreverie.co.uk/posts/why-delicious-reverie/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/why-delicious-reverie/</guid><description>I may have confused some of you with titling my personal site. So I&apos;m going to attempt to explain my decision and discuss perhaps the greatest work of fiction ever produced in either French or English...</description><pubDate>Wed, 11 Feb 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I may have confused some of you with titling my personal site &quot;Delicious Reverie&quot;. So I&apos;m going to attempt to explain my decision, and discuss perhaps the greatest work of fiction ever produced in either French or English...&lt;/p&gt;
&lt;h3&gt;Why not &lt;a href=&quot;http://benread.com/&quot;&gt;benread.com&lt;/a&gt;? &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-delicious-reverie/#why-not-benread.com&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Yeah. Check it out: &lt;a href=&quot;https://benread.com/&quot;&gt;https://benread.com&lt;/a&gt;. That&apos;s not me. That&apos;s the other guy, named Ben Read, who happens to be a web developer. I&apos;m sure he&apos;s a lot better at it than me.&lt;/p&gt;
&lt;p&gt;Hilariously enough, I was contacted by a headhunting organisation recently who thought I was him; and I thankfully realised and straightened him out before things got really interesting.&lt;/p&gt;
&lt;h3&gt;A State of Mind &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-delicious-reverie/#a-state-of-mind&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;So, I went with &lt;a href=&quot;http://deliciousreverie.co.uk/&quot;&gt;deliciousreverie.co.uk&lt;/a&gt; for this reason. At the time I conceived this blog, I was about 2 years into reading the formidable tome Les Miserables, in English, by Victor Hugo. I love that book. As well as the melancholy characters it contains, I loved the realism of France in the 1700s and was immediately drawn to Victor Hugo&apos;s thoughtful, at times poignant, prose.&lt;/p&gt;
&lt;p&gt;There&apos;s &lt;a href=&quot;https://books.google.co.uk/books?id=CNTT12PLXeEC&amp;amp;lpg=PP1&amp;amp;dq=les%20miserables%20victor%20hugo&amp;amp;pg=PA599#v=onepage&amp;amp;q=les%20miserables%20victor%20hugo&amp;amp;f=false&quot;&gt;one scene&lt;/a&gt; that I particularly loved, when a minor character, Monsieur Mabeuf, has been listening to his housekeeper relate a story from one of his old books. The old man appreciates the book anew in explaining to his housekeeper what she has been reading. The last line simply says:&lt;/p&gt;
&lt;h1&gt;&quot;And Monsieur Mabeuf fell into a delicious reverie&quot; &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-delicious-reverie/#%22and-monsieur-mabeuf-fell-into-a-delicious-reverie%22&quot;&gt;#&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;It&apos;s out of these delicious reveries—or silent meditations if you like—that so many of our good ideas come. There is little more poignant than sitting and considering, of allowing the mind to think freely and contemplate whatever it will: a particular problem we&apos;re facing, an action we&apos;re about to take, or just to take a minute to appreciate strange life.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Why &apos;Delicious Reverie&apos;?&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Why Performance Matters</title><link>https://deliciousreverie.co.uk/posts/why-performance-matters/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/why-performance-matters/</guid><description>Performance seems to be increasingly becoming a battleground for those of us who create websites. How has this situation arisen? How do we cope with these new requirements? How do we ensure our sites and web apps (sorry, Jeremy) don&apos;t suffer because of changing landscapes of user — and search engine — requirements. </description><pubDate>Tue, 06 Oct 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;Performance seems to be increasingly becoming a battleground for those of us who create websites. How has this situation arisen? How do we cope with these new requirements? How do we ensure our sites and web apps (sorry, Jeremy) don&apos;t suffer because of changing landscapes of user — and search engine — requirements.&lt;/p&gt;
&lt;p&gt;&quot;Everyone has broadband now. Therefore, we no longer need to be concerned about site load times&quot; said nobody, ever. But we may have assumed that the advent of much beeefier data plans would mean that load times could become a lower priority.&lt;/p&gt;
&lt;p&gt;This assumption ignores the fact that users could be on slower mobile networks. This has been discussed at length elsewhere. But a conversation I had recently highlighted another area that concerns a threat to the web as a whole.&lt;/p&gt;
&lt;h3&gt;When Mobile is Not Mobile &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-performance-matters/#when-mobile-is-not-mobile&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Mobile device users are unique in that they have a choice ... they have apps on their devices which might be able to perform a similar task to what they&apos;re trying to achieve on your website.&lt;/p&gt;
&lt;p&gt;A good example of this is a booking system. Does your website load — and respond — quickly enough for them? That window of opportunity &lt;a href=&quot;https://www.nytimes.com/2012/03/01/technology/impatient-web-users-flee-slow-loading-sites.html?_r=0&quot;&gt;is now measured in milliseconds&lt;/a&gt;, not seconds. Will they decide to use a booking system they already have on their device instead? Will they use a search, throwing up all kinds of alternative possibilities for an entertaining evening out.&lt;/p&gt;
&lt;p&gt;This &apos;user abandonment&apos; doesn&apos;t just impact on your site, on solely your service. It has wider implications.&lt;/p&gt;
&lt;h3&gt;A Performance Win is A Web Win &lt;a href=&quot;https://deliciousreverie.co.uk/posts/why-performance-matters/#a-performance-win-is-a-web-win&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If people are turning away from their browsers and towards their devices, then how is that going to impact the web in the future? Could the web eventually be relegated to a second-fiddle service, which users only turn to as a backup?&lt;/p&gt;
&lt;p&gt;Haven&apos;t we seen that model before though? When there was no wifi, where users would have apps on their local devices simply because they couldn&apos;taccess the web on the go. I remember taking screenshots of Google Maps data so that I could find my way around the wilds of Mid Wales. Although that&apos;s perhaps going back a step too far, I personally don&apos;t want to lose the web — not for all of its uncontrollable, unfathomable aggregation of data, truthful and otherwise, that abounds.&lt;/p&gt;
&lt;p&gt;The web is still the portal to knowledge, to education, to opinion. It&apos;s the unique frontier, where anything can happen. If we were to lose the sense of what that means to the average user, then we have lost something quite unique, perhaps forever.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Why Performance Matters&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Wordpress theming with Backbone.js</title><link>https://deliciousreverie.co.uk/posts/wordpress-themeing-with-backbone-js/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/wordpress-themeing-with-backbone-js/</guid><description>You&apos;ll notice that there&apos;s not a huge amount of posts about JavaScript on my site. That&apos;s not an accident, I&apos;m really only beginning to gain my feet with the language. Despite this I took the plunge and decided to build a Wordpress theme that uses Backbone.js to render the posts in a list. Here&apos;s a bit about the project.</description><pubDate>Wed, 06 Jan 2016 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;You&apos;ll notice that there&apos;s not a huge amount of posts about JavaScript on my site. That&apos;s not an accident, I&apos;m really only beginning to gain my feet with the language. Despite this I took the plunge and decided to build a Wordpress theme that uses Backbone.js to render the posts in a list. Here&apos;s a bit about the project.&lt;/p&gt;
&lt;p&gt;Backbone.js is one of the most stable of the Javascript frameworks, which is one of the reasons I chose to focus on it. I wanted to start with a stable framework that would allow me to get to grips with it properly, and that I didn&apos;t have to worry about a huge amount of refactoring when the next version came out. But it&apos;s also one of the smallest frameworks. Even with recommended dependencies jQuery and Underscore it&apos;s still faster than some of the larger frameworks out there. This appeals to my sense of what users are most in need of from us as developers.&lt;/p&gt;
&lt;h3&gt;Where I succeeded&lt;/h3&gt;
&lt;p&gt;I&apos;m going to talk as little as I can about the tools here. I&apos;m exhausted at having to learn someone else&apos;s toolset to enable me to accomplish my work. I&apos;m just going to say that it&apos;s good practice to break down your files into small blocks of code, this will really help you when it comes to debugging and maintenance of your code later on. So here&apos;s my JavaScript project folder setup:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-src
  |
  --js
    |
    ---1 underscore.js
    ---2 backbone.js
    ---3 models.js
    ---4 views.js
    ---5 collection.js
    ---6 router.js
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;note: I&apos;m loading jQuery in separately from a CDN.&lt;/p&gt;
&lt;p&gt;Structuring my project folder this way allows me to load the JS in the correct order. Now, when they get parsed by your task runner they&apos;ll be in the correct order to run.&lt;/p&gt;
&lt;p&gt;After a few false starts I had gotten the post feed into my view, and it is outputting as I wanted. This felt like such a victory! I can now see all of my posts and click through ... but this simple bit of functionality is currently far from finished.&lt;/p&gt;
&lt;h4&gt;Where I have yet to succeed &lt;a href=&quot;https://deliciousreverie.co.uk/posts/wordpress-themeing-with-backbone-js/#where-i-have-yet-to-succeed&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Documentation is patchy. I started off by being able to retrieve the basic JSON feed from /wp-json/, but couldn&apos;t find out why I can&apos;t see the posts. After some digging, I found out that the JSON endpoints haven&apos;t been implemented in Wordpress core yet. We still need to use the plugin. In fact, I enjoy what I do because I can research things easily on Stack Exchange. But there&apos;s not many people using Backbone with Wordpress, and I was distinctly on my own trying to find this out.&lt;/li&gt;
&lt;li&gt;No links. If you click on one of the links in the list view, Wordpress takes over and you will be shown the posts&apos; PHP page. This is because I have yet to wrap my head around how routing works.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These are some of the things I can&apos;t do yet. It really bothers me but until I find some time or it becomes a necessity, I can&apos;t justify more time on this project.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/endymion1818/questingtheair&quot;&gt;Here&apos;s the theme if you want to pick over my code.&lt;/a&gt;&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Wordpress theming with Backbone.js&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>Words Fail Me.</title><link>https://deliciousreverie.co.uk/posts/words-fail-me/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/words-fail-me/</guid><description>I&apos;m a great fan of Virginia Woolf&apos;s Stream of Consciousness writing, but that isn&apos;t why this post is here.</description><pubDate>Fri, 27 Feb 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;I&apos;m a great fan of Virginia Woolf&apos;s Stream of Consciousness writing, but that isn&apos;t why this post is here.&lt;/p&gt;
&lt;p&gt;As developers, the hardest thing to do is to name things. Why is that? Why is &quot;semantic code&quot; so hard to achieve.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=E8czs8v6PuI&quot;&gt;Virginia Woolf, in this recording from 1937 entitled &quot;Words Fail Me&quot;&lt;/a&gt; hits the nail squarely on the head.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:Words Fail Me.&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>How to test JavaScript API Calls</title><link>https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/</guid><description>In the January 2020 issue of Net Magazine, we walked through how to use React testing library to write basic unit tests for your React components. In this article I&apos;m going to dive a little deeper and show how to write tests for some code that fetches data from an API.</description><pubDate>Thu, 09 Apr 2020 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;In the January 2020 issue of Net Magazine, we walked through how to use React testing library to write basic unit tests for your React components. In this article I&apos;m going to dive a little deeper and show how to write tests for some code that fetches data from an API.&lt;/p&gt;
&lt;p&gt;This is an important distinction from what we covered previously because writing tests for UI components is very different from tests like this, and I hope that you&apos;ll learn some more things to help you ensure that all of your code is production ready, which will give you and your stakeholders more confidence when publishing new code.&lt;/p&gt;
&lt;h2&gt;Step 0. Decide What to Test &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#step-0.-decide-what-to-test&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before we even begin writing tests it&apos;s good to decide what needs to be tested. We need to set clear boundaries before we begin, otherwise we could waste time writing tests unnecessarily. Read through your code and see what different outcomes might be generated by your code.&lt;/p&gt;
&lt;p&gt;In our example of fetching data from an API, the API call could be successful, that counts as one outcome. But what if it&apos;s not successful? And what should happen if the call is successful, but it returns no data? That&apos;s three different possible outcomes already!&lt;/p&gt;
&lt;p&gt;Let&apos;s look at our imaginary API call to see what outcomes exist. Here&apos;s the code we&apos;re going to test:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import env from &quot;./ENV&quot;;
import axios from &quot;axios&quot;;

const getApiData = (
  parameters = {}, 
  domain = env.domain
) =&amp;gt;  axios.get(`${domain}/api/v1/data/?${parameters}`).then(function (response) {
  // handle success
  if (!Array.isArray(data) || !data.length) {
    return [];      
  }
  return data;
  }).catch(function (error) {      
    // handle error      
    console.log(error);   
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking at my code, I can see the following outcomes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fetch api data&lt;/li&gt;
&lt;li&gt;Fetch data with parameters specified&lt;/li&gt;
&lt;li&gt;Return the data if the call was successful&lt;/li&gt;
&lt;li&gt;Return an empty array if no data was received&lt;/li&gt;
&lt;li&gt;Log an error if the request was unsuccessful&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Looking at your code in the beginning like this often reveals other issues to you that you may not have noticed before, and which prompts you to revisit your original code and improve it.&lt;/p&gt;
&lt;p&gt;Let&apos;s handle the first 4 tests first, then come back to the last two and see how we can improve our code.&lt;/p&gt;
&lt;p&gt;To begin, I&apos;ll create a new file to write my tests in. The name of the file is usually the same as the module. So if my module is called GetApiData.js, my test should be GetApiData.test.js.&lt;/p&gt;
&lt;h2&gt;Setup and Mocking &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#setup-and-mocking&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;1. Mock the API &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#1.-mock-the-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although this test is about fetching data from the API, I don&apos;t want to actually call the data from the API. There are several reasons for this: Primarily, it&apos;s because I&apos;m not testing the API, I&apos;m testing the code I have written. But also there could be a cost involved each time I contact the API, I don&apos;t want or need that cost to be incurred. Finally, I don&apos;t want to wait for the API query to resolve for my tests to finish!&lt;/p&gt;
&lt;p&gt;To do that, I&apos;m going to &quot;mock&quot; this function. When you &quot;mock&quot; something you essentially overwrite the function with a fake function. Let&apos;s first import the code that was written to fetch data from that API, and also the library that we used to connect to the API, Axios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import GetApiData from &quot;./GetApiData&quot;;
import axios from &quot;axios&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After importing it, we can overwrite the functionality of axios like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jest.mock(&quot;axios&quot;);

const mockedAxios = axios.get;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, every time we call GetApiData in this file, and that calls Axios, it&apos;ll use our mocked implementation. Using it in the variable mockedAxios will help us identify clearly what we&apos;re doing when we write our tests.&lt;/p&gt;
&lt;p&gt;The last thing we want to set up in regard to our API is the domain. This would be a parameter that is passed via our configuration, or part of our environment variables. But we&apos;re not testing our environment variables, so we should mock that domain too:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const domain = &quot;https://fakeapi.com/&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Mock the console &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#2.-mock-the-console&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The next thing we want to mock is what we would have used in our code to log out errors: console.log(), for similar reasons we mentioned above: we&apos;re not testing the functionality of the console. Also, we don&apos;t want to actually log the errors to the console as we&apos;re running tests, but instead somewhere we can test the output.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const mockedConsole = jest.spyOn(global.console, &quot;error&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By using Jest&apos;s SpyOn function, we can examine when that function was called, and what it was called with ... it&apos;s actually is a spy function, reporting back to us (thankfully!).&lt;/p&gt;
&lt;h3&gt;3. Mock the data that should be returned &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#3.-mock-the-data-that-should-be-returned&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, because we&apos;re not contacting the api, we need to provide mocked data to test against as if though it did:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const mockedDataOne = {
  id: 1234,
title: &quot;Super Blog Post&quot;,
categories: [&quot;1&quot;],
_embedded: {
  
  term: [[{ name: &quot;Category&quot; }]],

author: [{ name: &quot;Author&quot; }],
},};const mockedDataTwo = {
  id: 165,
title: &quot;Super Post Two&quot;,
categories: [&quot;2&quot;],
_embedded: {
  
  term: [[{ name: &quot;Category&quot; }]],

author: [{ name: &quot;Author&quot; }],
},};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right! Let&apos;s begin our tests with a wrapping description:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;describe(&apos;GetApiData() Source data so we can consume it&apos;, () =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. Clean ups &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#4.-clean-ups&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Last piece of setup here: we want to reset our mocked API call and console log before each new test, otherwise we&apos;ll have stale data left over from the previous test, which could cause subsequent tests to fail:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;beforeEach(() =&amp;gt; {
  mockedAxios.mockReset();
  mockedConsole.mockReset();
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Right, now we&apos;ve set up our tests, and mocked the important stuff, let&apos;s dive into our first test ...&lt;/p&gt;
&lt;h2&gt;Test 1: Fetch api data &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#test-1:-fetch-api-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&apos;s begin our tests with a wrapping description:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;describe(&apos;GetApiData()&apos;, () =&amp;gt; {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This wrapping function describes the component, or makes a short statement to help us understand what these tests are for. If your function name adequately describes what it does, and you don&apos;t need a longer description, that&apos;s a good sign that you have named your function well!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;Should get api data&quot;, async () =&amp;gt; {
  mockedAxios.mockResolvedValueOnce({ 
    data: [{ test: &quot;Hi I worked!&quot; }] 
  });
  const data = await getApiData(domain);
  expect(mockedAxios).toBeCalledTimes(1);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First thing to note: this is an asynchronous function! axios.get is already an async function so it makes sense to test it asynchronously too. It&apos;s best to make api calls async because you have a callback even if something fails, rather than the request simply hanging indefinitely, which is bad for user experience.&lt;/p&gt;
&lt;p&gt;mockResolvedValueOnce() is a built-in function in Jest that, well, mocks the resolved value of the API call just once.&lt;/p&gt;
&lt;p&gt;Here we&apos;re mocking the result of the mocked axios call. We&apos;re not testing the contents of the data, so I&apos;ve just added a dummy object to the result of the mockResolvedValueOnce() function, since that&apos;s adequate for what we&apos;re testing.&lt;/p&gt;
&lt;p&gt;You can now run this test, and you should see 1 passing test. Go you!&lt;/p&gt;
&lt;p&gt;So ... it worked! We can stop there right?&lt;/p&gt;
&lt;p&gt;Well ... how do we know our code contacted the right API endpoint? How do we know it sent the correct parameters, if we need any?&lt;/p&gt;
&lt;h2&gt;Test 2: Return the data if the call was successful &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#test-2:-return-the-data-if-the-call-was-successful&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Our next test will check that we have the data we expected in the return value of the GetApiData() function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&apos;Should get data from the api&apos;, async () =&amp;gt; {	mockedAxios.mockResolvedValueOnce({ 
  data: [ mockedDataOne, mockedDataTwo ] 
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This time we&apos;re mocking the return value containing the two objects we originally set up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const data = await getApiData(domain);
expect(mockedAxios).toBeCalledTimes(1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just as before, I like to check that we did actually call the mockedAxiosfunction. Next I&apos;m going to check one of the data objects to make sure it has the same id as mockedDataOne:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;expect(data[0]).toEqual(
  expect.objectContaining({
    id: mockedDataOne.id
  })
)})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could do more tests, perhaps making sure that data[1] also has the corresponding ID, but this is enough to convince me that the data is returning correctly.&lt;/p&gt;
&lt;p&gt;Now this does seem a little ... &quot;circular&quot; at first. You might think &quot;of course it contains it! That&apos;s what you told it to contain!&quot;, but think about it for a minute: we haven&apos;t just returned that data. We&apos;ve used our preexisting code (minus the actual API calls and real data) to return it. It&apos;s like throwing a ball, then our code caught it, and threw it back.&lt;/p&gt;
&lt;p&gt;If nobody threw our ball back, then something is very wrong with the code we&apos;re testing: it&apos;s not working as we expected.&lt;/p&gt;
&lt;h2&gt;Test 3: Fetch data with parameters specified &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#test-3:-fetch-data-with-parameters-specified&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&apos;s our next assertion. We want to make sure our code passed the parameters we wanted, and returned the value we expected.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
it(&apos;should get data using parameters&apos;, async () =&amp;gt; {
    const params = {
    categories: [&apos;2&apos;],
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So this time our params contain an array specifying category 2 should be fetched. Remember we mocked some data in our setup? How many of those mocked data sets has the category of 2? Only one of them:mockedDataTwo.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mockAxios.mockResolvedValueOnce({ 
  data: mockedDataTwo 
})
await GetApiData(domain, params)
expect(mockAxios).toHaveBeenCalled()
expect(mockAxios).toBeCalledWith(`${domain}/api/v1/data/`, {
  params: {
    categories: params.categories,
  },
})
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Okay, so if this test passes, our code is passing the categories correctly. Great! But does the data reflect that?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;expect(data[0]).toEqual(  expect.objectContaining({    categories: [&quot;2&quot;],  }));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If this test passes, then great! We have successfully obtained data with the correct parameters.&lt;/p&gt;
&lt;p&gt;Another check to do here is that the data only contains items with this category, and not any other. I&apos;ll leave that one for you to figure out.&lt;/p&gt;
&lt;p&gt;These next two tests are to verify we have captured two significant branches, or outcomes, of our code: failures.&lt;/p&gt;
&lt;h2&gt;Test 4: Return an empty object if no data was recieved &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#test-4:-return-an-empty-object-if-no-data-was-recieved&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If there hasn&apos;t been any data sent back to us after the API call, we have returned an array as a fallback so that we don&apos;t have an exception in our data layer. that can be used by our UI to provide a fallback - once the API call has been resolved.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;Should return an empty array if no data was recieved&quot;, async () =&amp;gt; {  const data = await GetApiData(domain, params);  mockAxios.mockResolvedValueOnce({ data: null });  expect(mockAxios).toBeCalledTimes(1);  expect(Array.isArray(data)).toBeTruthy;});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re mocking a data object with a null value here to represent no values being returned from the API call. We&apos;re using Array.isArray because that is far more robust than using isArray, which is an older method that returns true for a number of different cases (don&apos;t ask...).&lt;/p&gt;
&lt;h2&gt;Test 5: Log an error if the request was unsuccessful &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#test-5:-log-an-error-if-the-request-was-unsuccessful&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Logging errors is a vital part of a robust application. It&apos;s a great way of being able to respond to API failures or application exceptions before users get to see them. In this test, I&apos;m just going to check for a console.log()call, but in a production app, there would be an integration with some external logging system that would send an email alert to the dev team if it was a critical error:&lt;/p&gt;
&lt;p&gt;Our final test uses our consoleMock from our initial setup (see above):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;it(&quot;Should log an error if the request was unsuccessful&quot;, async () =&amp;gt; {  const error = new Error(&quot;there was an error&quot;);  mockAxios.mockRejectedValue(error);  await GetApiData(domain);  expect(mockAxios).toBeCalledTimes(1);  expect(mockedConsole).toBeCalledTimes(1);  expect(mockedConsole).toBeCalledWith(error);});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the consoleMock function allows us to mock the functionality of the console.log object. Because we&apos;re testing that an error is thrown by our code, we need to use the Error object to test the output correctly.&lt;/p&gt;
&lt;p&gt;So there we are ... we now have a suite of tests to give us more confidence that our code is production ready ... as long as the tests don&apos;t fail in our pipeline, we can be confident that we have met the core criteria for our GetApiData function.&lt;/p&gt;
&lt;h2&gt;Conclusion &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There&apos;s a lot to these functions and it can take quite a bit of time to get used to writing this much code:- more than our actual function! But what is the price of confidence? ... if you think about it, by spending the time writing this code, we could have saved our company hundreds of thousands of pounds from lost income if it was broken!&lt;/p&gt;
&lt;p&gt;I would say that thoroughly testing your code is an important step, along with static typing, quality checking, and pre-release validation, to ensuring that your code is indeed production ready!&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: The price of confidence &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#boxout:-the-price-of-confidence&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Developers will spend more time writing tests than writing the components they’re building. That makes sense if you think about it: you need to test every possible outcome of the code that’s being written. As is demonstrated in this article, one API call with some basic functionality can result in a number of differing outcomes.&lt;/p&gt;
&lt;p&gt;The benefit of adding tests to your code can easily override the time spent by developers following this practice. If your business or customers needs the confidence that things won’t break, then testing is definitely a good practice to introduce at the start of a project.&lt;/p&gt;
&lt;p&gt;Other ways that testing can benefit a project include during refactors. Often project requirements will change after the code has been written. That introduces more risk into the codebase because on revisiting the code a developer might decide to refactor to make it simpler … which could include deleting things that were actually needed! Looking at the test serves as documentation: developers can see that there was a decision behind every code outcome that has been written.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Boxout: Scoping outcomes &lt;a href=&quot;https://deliciousreverie.co.uk/posts/writing-tests-for-js-api-calls/#boxout:-scoping-outcomes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The hardest part of finding out what to test is knowing what your code actually does. This becomes harder with the more time that passes between when you write tests to when you write the actual code. So I recommend writing tests alongside the component, or even before you write your component.&lt;/p&gt;
&lt;p&gt;When you’re doing this you’ll be more clearly able to think about all of the different outcome possibilities that your code offers: what variables might change? What different return values are possible?&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:How to test JavaScript API Calls&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item><item><title>2015 Year in review</title><link>https://deliciousreverie.co.uk/posts/year-in-review-2015/</link><guid isPermaLink="true">https://deliciousreverie.co.uk/posts/year-in-review-2015/</guid><description>For the past year and a bit, I&apos;ve worked at Blaze Communication, a marketing agency in Enfield. I&apos;ve learned so many new things at this company. My knowledge and understanding is far, far different than it was a year ago. Here are some of my observations.</description><pubDate>Thu, 10 Sep 2015 00:00:00 GMT</pubDate><content:encoded>
  &lt;p&gt;For the past year and a bit, I&apos;ve worked at Blaze Communication, a marketing agency in Enfield. I&apos;ve learned so many new things at this company. My knowledge and understanding is far, far different than it was a year ago. Here are some of my observations.&lt;/p&gt;
&lt;p&gt;All of these things could be blog posts in their own right; but I&apos;m a fan of brevity.&lt;/p&gt;
&lt;h3&gt;How to Sell Change &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#how-to-sell-change&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Right when I started working for Blaze, I got really annoyed and frustrated because I wanted to change how things were done to the way I was used to ... I&apos;d come from an organization working to very different ways of planning and development, and I wanted to implement all of them straight away.&lt;/p&gt;
&lt;p&gt;I soon found out that change needed to be justified in terms of benefits. So I looked for opportunities to raise the issues I was most concerned about.&lt;/p&gt;
&lt;p&gt;After a few strong - but not unpleasant - conversations in which I challenged the validity of certain processes, I got some of what I wanted.&lt;/p&gt;
&lt;p&gt;But I also had to see things from the company&apos;s perspective. They weren&apos;t interested in implementing things unless they could be convinced of a clear benefit. And I realised that some of the things I wanted to do weren&apos;t going to fit.&lt;/p&gt;
&lt;p&gt;I guess I learned that Agile also needs to be Agile.&lt;/p&gt;
&lt;h3&gt;How to Convince with Tact &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#how-to-convince-with-tact&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It was clear from day one that design skills (or UX skills) were not to be part of my role. However, I could see a gap in the skills of the team when it came to understanding the users&apos; perspective.&lt;/p&gt;
&lt;p&gt;I needed to find a way of doing this tactfully, so I wouldn&apos;t offend their sensibilities. I eventually succeeded in doing this by being patient, building trust with my design colleagues, and complimenting them when I felt there was a basis for doing so.&lt;/p&gt;
&lt;p&gt;Eventually, I was glad to see more talk of &quot;what&apos;s best for the user&quot; near the end of my tenure. I really hope they continue to learn UX based thinking after I move on.&lt;/p&gt;
&lt;h3&gt;How to perform basic SysAdmin tasks &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#how-to-perform-basic-sysadmin-tasks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The answer: With Great Caution. The first time I transferred in a domain from somewhere else, I knocked a site out for a few days. Also: because they say DNS propagation can take up to 48 hours doesnt mean it will. It&apos;s usually done in an hour or so.&lt;/p&gt;
&lt;p&gt;In the last few days I&apos;ve finished setting up a new server for the company, although a third party did a lot of the leg work it gave me a lot of insight to how these things happen.&lt;/p&gt;
&lt;p&gt;Deciding which version of PHP to install, and the different methods of running it, was a real eye opener for me.&lt;/p&gt;
&lt;p&gt;Migrating a server with 20+ live sites and many DNS records can be fun :-)&lt;/p&gt;
&lt;h3&gt;How to effectively Maintain older sites &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#how-to-effectively-maintain-older-sites&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The main thing is, don&apos;t be too precious about them. If you need to add a lot of extra Javascript just to get things to perform the way the beief describes, do it.&lt;/p&gt;
&lt;p&gt;If it&apos;s an older site and otherwise badly needs updating, it shows that the one responsible doesn&apos;t care too much about it&apos;s upkeep. So why should you invest your care to a greater degree?&lt;/p&gt;
&lt;h3&gt;How to use CSS Animations &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#how-to-use-css-animations&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Because design thinking wasn&apos;t part of my remit, I had to find another outlet for my creative thinking. I found plenty of scope in CSS Animations.&lt;/p&gt;
&lt;p&gt;When I could see from a design a case for animating something, I would seek a way of doing that in a subtle way. I found that softening page transitions and hover states gave me a way of using time as a canvas.&lt;/p&gt;
&lt;h3&gt;Other stuff &lt;a href=&quot;https://deliciousreverie.co.uk/posts/what-i-learned-this-year-2015/#other-stuff&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;How to build a Wordpress theme from scratch, how to use SASS, how to remove a hacked site from blacklists (once cleaned), how to build extensibly, how to manage &amp;amp; support remote developers, how to build a theme that&apos;s easily translateable, how to train clients in the use of their CMS.&lt;/p&gt;
&lt;p&gt;And probably tonnes more things.&quot;&lt;/p&gt;

  &lt;hr /&gt;
  &lt;footer&gt;
  &lt;p&gt;Thanks for reading this article via RSS. Let me know what you think by &lt;a href=&quot;mailto:endymion1818@gmail.com?subject=re:2015 Year in review&quot;&gt;sending me an email.&lt;/a&gt;&lt;/p&gt;&lt;/footer&gt;
</content:encoded></item></channel></rss>