<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Andrew Walpole&apos;s Blog</title><description>A blog about web development, engineering leadership, digital strategy and the occasional off-topic rant.</description><link>https://andrewwalpole.com/</link><language>en-us</language><item><title>Previews Are Hard! Also, Web Mullets</title><link>https://andrewwalpole.com/blog/previews-are-hard-also-web-mullets/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/previews-are-hard-also-web-mullets/</guid><description>A quick take on one of Jamstack&apos;s biggest hurdles it has yet to fully overcome: previewing content.</description><pubDate>Wed, 04 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Previews Are Hard! Also, Web Mullets&lt;/h1&gt;
&lt;p&gt;I love the Jamstack revolution that has been going on for the last few years. Statics sites, yes please! SSR, yes please! Headless CMS, let&apos;s freaking go! Netlify, Vercel, AWS Amplify, Cloudflare pages, tears, folks, tears. of. joy. But this soup of delicious tech, like all the others, has its souring points. The one I&apos;m most particularly interested in calling out here is content previewing.&lt;/p&gt;
&lt;p&gt;Living that agency life, most of the time we&apos;re just building websites for less technical folks to manage. Sure we&apos;re going to give them a great-looking, fast, functional website, but they also need to change that content, swap those images and relabel all the CTAs. Unfortunately, my findings are: &lt;em&gt;Jamstack stacks have poor implementations of preview workflows&lt;/em&gt;, and in many cases, implement them as afterthoughts, if at all.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;&amp;lt;img class=&quot;half&quot; src=&quot;/static/blog/spiderman-points-for-previews.jpg&quot; alt=&quot;Three-way spiderman pointing meme with Headless CMS and Framework pointing at each other and Host pointing at both of them&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;The problem seems to be in the decoupled nature of the parts. It&apos;s &lt;strong&gt;nice&lt;/strong&gt; that Jamstack makes it easy to pick and choose your CMS and your framework and your host almost fully independent of each other. The problem is that it likely takes at least two of those to work together non-trivially to achieve good previews. And, well, shoot. Now your stack is coupled, you&apos;re bought-in, on the hook, you&apos;ve become... &lt;em&gt;opinionated&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Opinions get things done, but they absolutely subdivide your target audience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s not the worst thing for you, lovely developer-reader-friend, but it seems like very few of these stack-players are actually willing to get opinionated themselves and work together to create that great previewing experience. Instead, they try to go after universalized solutions, which lead to so much hoop-jumping for us. I will call out &lt;a href=&quot;https://prismic.io&quot;&gt;Prismic&lt;/a&gt; though; I think they really gave it a go, but even their framework-specific preview SDKs have spun my head around a bit; they haven&apos;t quite cracked it.&lt;/p&gt;
&lt;h2&gt;The Solution We Desperately Need&lt;/h2&gt;
&lt;p&gt;Wordpress... sorta! The block editor, that thing freaking rules, except, well it runs on React and I like Vue. But that aside, clients &lt;em&gt;love&lt;/em&gt; building pages with blocks and editing content in blocks. Even ACF blocks, which I use most, are a great experience: Content inputs on the right, instant previews on the left. But this all falls apart when you want to use Wordpress in a Jamstacky way. Your block content is just walled-off enough from the Wordpress API that I can&apos;t easily just query data into my component and instantly render it to you as a preview.&lt;/p&gt;
&lt;p&gt;Chris Coyier tweeted it perfectly just a few weeks ago:&lt;/p&gt;
&lt;p&gt;&amp;lt;blockquote class=&quot;twitter-tweet&quot;&amp;gt;&amp;lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&amp;gt;Sure, I could do WordPress-as-content-API-only thing, but ughgk, I don&apos;t love it. It&apos;s twice the technical debt and doesn&apos;t really provide a best-of-both-worlds thing. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;I just want a single product that is WordPress in the back and Astro in the front. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Ship it people.&amp;lt;/p&amp;gt;— Chris Coyier (@chriscoyier) &amp;lt;a href=&quot;https://twitter.com/chriscoyier/status/1513593685925056514?ref_src=twsrc%5Etfw&quot;&amp;gt;April 11, 2022&amp;lt;/a&amp;gt;&amp;lt;/blockquote&amp;gt; &amp;lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;Wordpress in the back and Astro in the front – seamless previews transitioning into beautiful static builds.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A metaphorical web mullet to stun the masses, if you will.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is what we need.&lt;/p&gt;
&lt;h3&gt;P.S. 👀&lt;/h3&gt;
&lt;p&gt;&amp;lt;blockquote class=&quot;twitter-tweet&quot;&amp;gt;&amp;lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&amp;gt;okay :)&amp;lt;/p&amp;gt;— fred (@FredKSchott) &amp;lt;a href=&quot;https://twitter.com/FredKSchott/status/1513593963457880066?ref_src=twsrc%5Etfw&quot;&amp;gt;April 11, 2022&amp;lt;/a&amp;gt;&amp;lt;/blockquote&amp;gt; &amp;lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
&lt;p&gt;Save us Fred!&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>First</title><link>https://andrewwalpole.com/blog/first/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/first/</guid><description>Hello World! This is my first post on my new website and blog. Like and subscribe folks!</description><pubDate>Sat, 30 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;First&lt;/h1&gt;
&lt;p&gt;Well, I did it; after two years and three scrapped site designs – and of course I&apos;m not even that stoked on this one – the itch to blog has been growing, so here I&apos;ve settled to make a go at it.&lt;/p&gt;
&lt;p&gt;This site is built with eleventy, with a custom esbuild setup for the small bits of sass and javascript. It&apos;s hosted on Cloudflare Pages, which is a bit of a departure from my usual choice of vercel or netlify, but I&apos;ve been really happy with Cloudflare workers, so I&apos;m giving this a go as well!&lt;/p&gt;
&lt;h3&gt;What can you expect from this blog?&lt;/h3&gt;
&lt;p&gt;Well, probably just a decent bit of web-related punditry and maybe a bit of leadership stuff... Really whatever strikes my fancy I guess. My goal is to publish a healthy mix of useful tech-specific things along with my own views on the way the web development industry is moving, with some sprinkling in of random things I like. If that&apos;s your jam, welcome, feel free to like &lt;em&gt;(over there ↘)&lt;/em&gt; and &lt;a href=&quot;/feed.xml&quot;&gt;subscribe&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;More specifically these topics have a high chance of showing up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web Development&lt;/li&gt;
&lt;li&gt;Dev Stacks/Architecture&lt;/li&gt;
&lt;li&gt;Serverless non-sense&lt;/li&gt;
&lt;li&gt;Leading dev teams&lt;/li&gt;
&lt;li&gt;Business strategy&lt;/li&gt;
&lt;li&gt;Robust-first computation via living-systems&lt;/li&gt;
&lt;li&gt;3D printing tips and processes&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/first.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Living on the Edge Functions</title><link>https://andrewwalpole.com/blog/living-on-the-edge-functions/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/living-on-the-edge-functions/</guid><description>Let&apos;s get a surface-level view of edge functions: what they are, what they&apos;re good for, and how you can get started with them</description><pubDate>Tue, 03 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Living on the Edge Functions&lt;/h1&gt;
&lt;p&gt;Edge Functions are all a buzz at the moment. &lt;a href=&quot;https://www.netlify.com/blog/announcing-serverless-compute-with-edge-functions/&quot;&gt;Netlify just got them&lt;/a&gt;, they&apos;re &lt;a href=&quot;https://vercel.com/docs/concepts/functions/edge-functions&quot;&gt;in beta over at Vercel&lt;/a&gt;, and &lt;a href=&quot;https://developers.cloudflare.com/workers/&quot;&gt;Cloudflare has had them&lt;/a&gt; for a few years under the somewhat misleading name, Cloudflare Workers.&lt;/p&gt;
&lt;p&gt;It&apos;s easy to confuse them with serverless functions, and in my mind, I think they are a type of serverless function. The main difference is in the name; edge functions run as close to the requestor as they can (on a distributed edge server), while serverless functions are deployed to a specific region where they run regardless of the initiator&apos;s origin.&lt;/p&gt;
&lt;p&gt;Beyond that universal difference, the specific organizations offering edge functions may implement different runtimes and add a proprietary layer on top of their usage that is less consistent. Or said another way, edge functions and serverless functions could likely work exactly the same, but in an effort to provide a differentiated offering, edge functions end up being optimized for specific edge-efficient use-cases. Generally, that use-case is: Intercept a client request and &lt;code&gt;DO STUFF&lt;/code&gt; before returning a response; where &lt;code&gt;DO STUFF&lt;/code&gt; is a whole heaping world of possibilities.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a quick look at what Netlify, Vercel and Cloudflare offer, as well as some of the great use-cases you can explore with edge functions.&lt;/p&gt;
&lt;h3&gt;Netlify&lt;/h3&gt;
&lt;p&gt;Netlify&apos;s edge function solution comes in one surprising flavor – Deno runtime! As a sideline-cheerleader for Deno, I was pleasantly surprised to see that Netlify went this direction. While Deno could be a whole post on its own, don&apos;t be scared off; you can write JavaScript and TypeScript in Deno, plus get access to a very cool import system, which in comparison to npm, makes bringing in external resources a breeze.&lt;/p&gt;
&lt;p&gt;No surprise, Netlify provides a pretty great DX for edge functions. You describe the routes that should run edge functions in your &lt;code&gt;netlify.toml&lt;/code&gt; and declare the functions themselves in an &lt;code&gt;edge-functions&lt;/code&gt; directory at the top level of your project. Netlify handles the rest within its continuous deployment pipeline.&lt;/p&gt;
&lt;p&gt;Finally, it&apos;s worth noting that Netlify really leans into the pitch that even if you aren&apos;t directly using them, your favorite frameworks – Next.js, Nuxt.js, Eleventy, Astro, SvelteKit, Remix – can or are taking advantage of them for you by providing middleware that use edge functions to SSR content for clients blazingly fast.&lt;/p&gt;
&lt;h3&gt;Vercel&lt;/h3&gt;
&lt;p&gt;Unfortunately, at the moment there isn&apos;t too much to report here. Edge functions are in beta on Vercel and are only implemented for Next.js projects. However, as a fan and an avid user of Vercel serverless functions, I expect that as they do come up to speed with their competition, it will be a solid offering.&lt;/p&gt;
&lt;h3&gt;Cloudflare Workers&lt;/h3&gt;
&lt;p&gt;One nice thing (among many) about Cloudflare, is that even if you&apos;re hosting through another platform, as a DNS manager, you can use Cloudflare in conjunction with whatever your current setup is. So in most cases, unless you are deeply tied into another DNS management system, you can add this on to any site with minimal disruption. Once you have your domain using Coudflare you&apos;re ready to use their &lt;s&gt;edge functions&lt;/s&gt;, err, I mean Cloudflare workers.&lt;/p&gt;
&lt;p&gt;Using Cloudflare workers is not bad, it&apos;s also not a one-click deploy, but I think that&apos;s just fine. You create workers on your account and you can either set up their Wrangler CLI tool to write and deploy workers, or you can use an online editor, which I have found to be a really great starting path, providing you a decent-enough editor along with side-by-side testing and debugging tools.&lt;/p&gt;
&lt;h2&gt;Edge Function Use-Cases&lt;/h2&gt;
&lt;p&gt;So what do you do with edge functions? Probably too many things to list, but we&apos;ll try anyway, which should help to also solidify the concept of them.&lt;/p&gt;
&lt;p&gt;Remember, edge functions run after the client/user makes a request (that you have defined to intercept). The function has some sort of payload with the request information as well as what the response will be. Within the function is where the magic happens, you could do things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Check the request parameters, query a database, and apply the data into the response.&lt;/li&gt;
&lt;li&gt;Check the country the request came from (both Netlify and Cloudflare provide GeoIP services) and return a redirection to a specific URL.&lt;/li&gt;
&lt;li&gt;Increment a counter in a database (or better yet, Cloudflare KV which is a whole blog post in itself) to track page visits.&lt;/li&gt;
&lt;li&gt;Add/modify a header value (great for Auth things).&lt;/li&gt;
&lt;li&gt;Read/Write/Manage cookies.&lt;/li&gt;
&lt;li&gt;Completely compile, render and return a page or component with injected data/content.&lt;/li&gt;
&lt;li&gt;Inject some HTML or a JS script into the page.&lt;/li&gt;
&lt;li&gt;Respond with some JSON like an API endpoint.&lt;/li&gt;
&lt;li&gt;Enforce a block or allow-list.&lt;/li&gt;
&lt;li&gt;Respond with an a/b test of content.&lt;/li&gt;
&lt;li&gt;Respond with an image or other types of content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see a bunch of these and more in action with Cloudflare workers &lt;a href=&quot;https://developers.cloudflare.com/workers/examples/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;tl;dr&lt;/h3&gt;
&lt;p&gt;Edge functions are serverless functions, but since they&apos;re on the edge, you may want to use them for specific scenarios that make sense on the edge, like modifying the response of a request by some smallish amount before sending it back. They are playing a big role in allowing frameworks to do high-speed SSR and if you haven&apos;t tried them out yet, or haven&apos;t even stepped into the world of serverless, Netlify and Cloudflare have made getting started really easy.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/living-on-the-edge-functions.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Throw the dart</title><link>https://andrewwalpole.com/blog/throw-the-dart/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/throw-the-dart/</guid><description>Don&apos;t get stuck for too long thinking about, just go ahead and throw it.</description><pubDate>Thu, 05 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Throw the dart&lt;/h1&gt;
&lt;p&gt;You don’t get good at darts by holding on to a dart, analyzing it’s form, composition, weight, learning about its history, the dart production, packaging and distribution process, binge-watching hundreds and hundreds of dart-throwing videos on Youtube, showing the dart to friends and dart enthusiasts and philosophizing about how it works and how it will fly when it is thrown. Then, at the last possible second, when the game has started, when it counts, when it has to be good, &lt;em&gt;when it can’t fail&lt;/em&gt;, you throw the dart.&lt;/p&gt;
&lt;p&gt;All of that education and preparation you did may help a little, you might not miss the board. But I hope you agree with me and see that there is a much more efficient way to get better at darts; to compete when it counts.&lt;/p&gt;
&lt;h3&gt;Throw the dart. Early. Often. Again, and again and again.&lt;/h3&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>What You&apos;ve Done is What You&apos;ll Do</title><link>https://andrewwalpole.com/blog/what-youve-done-is-what-youll-do/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/what-youve-done-is-what-youll-do/</guid><description>A quick riff on an idea that has stuck with me for a bit and can really help if you lean into it.</description><pubDate>Mon, 09 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;What You&apos;ve Done is What You&apos;ll Do&lt;/h1&gt;
&lt;p&gt;I can&apos;t claim these words, but they&apos;ve stuck with me from an old (2009 maybe?) Adobe MAX talk by &lt;a href=&quot;https://twitter.com/natzke&quot;&gt;Erik Natzke&lt;/a&gt;, and so I thought I might pass them forward here.&lt;/p&gt;
&lt;p&gt;I love learning new things. Being a part of the web industry right now, it&apos;s essential to constantly be learning new things if you want to keep up with the latest and greatest. But there&apos;s a cost to focusing on that idea too much. Worrying about figuring out how to keep up can be a huge emotional drain, and with all the avenues there are to explore it&apos;s quite easy to burn-out on the burden of learning.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&apos;What you&apos;ve done is what you&apos;ll do&apos; manifests for me in my daily life in two main ways:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Scoped Learning&lt;/h3&gt;
&lt;p&gt;First, it reminds me that when there is something new to learn, I can break it up into two major buckets: &quot;Do I want to know &lt;em&gt;how to do&lt;/em&gt; this?&quot; or &quot;Do I just want to know &lt;em&gt;about&lt;/em&gt; this?&quot;&lt;/p&gt;
&lt;p&gt;The latter question carries a ton of utility while removing a lot of the burden that comes along with learning how to do something. If I can hear a business case from a client or even coming from my own ideas and think of a technology I may &lt;em&gt;know about&lt;/em&gt; that fits the bill, that&apos;s all I need in that moment; the doing can come later if it needs to.&lt;/p&gt;
&lt;p&gt;Most importantly, I find that explicitly setting this scope of learning for myself up-front provides a boost to my drive to dig-in, because the expectations are set; I know, generally, the size of the hill I&apos;m about to climb and so I can prepare accordingly for the trek.&lt;/p&gt;
&lt;h3&gt;Power in Doing&lt;/h3&gt;
&lt;p&gt;Don&apos;t get me wrong, the last paragraph was all about &lt;em&gt;NOT DOING&lt;/em&gt; if you don&apos;t have to, but I think closer to Erik&apos;s point in his talk, this idea is also all about leaning in extra on what you know how to do already.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you have a hammer, everything looks like a nail, and that&apos;s ok!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You&apos;ve put that extra learning time into what you do. You wield your craft. &lt;em&gt;Be proud of it&lt;/em&gt;, and then find the places you can keep doing it. The whole generalist vs. specialist thing sort of rubs me the wrong way, because as a self-proclaimed web generalist, I find that I have a very specific set of skills: the things I actively &lt;em&gt;do&lt;/em&gt;! And I think that&apos;s actually true of most folks.&lt;/p&gt;
&lt;p&gt;If you ask me to build a site, I&apos;ll probably use Eleventy or Vue or just vanilla HTML/CSS/JS, &lt;em&gt;I&apos;ve done that&lt;/em&gt;. If you want to start an online store, I might push you over to Etsy, &lt;em&gt;I&apos;ve done that&lt;/em&gt;. If you need some &lt;a href=&quot;https://art.andrewwalpole.com&quot;&gt;generative art&lt;/a&gt; I might make it with P5.js or Processing, &lt;em&gt;I&apos;ve done that&lt;/em&gt;. Need an API? I&apos;ll build you one with serverless functions, &lt;em&gt;I&apos;ve done that&lt;/em&gt;. Want a custom 3D printed trophy, I&apos;ll design it in illustrator and fusion 360, slice it with Cura and print it on my Ender 3 Max printer, because &lt;em&gt;I&apos;ve done that&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Part of it is that, yes, I put in the time and got experience in doing these things, but the other important piece is that getting to that point also means I must have had a good experience, I must have liked it, at least somewhat, to the point that I can continue to come back to these skills as the core of what I do.&lt;/p&gt;
&lt;p&gt;Sure, I&apos;ll learn to do more things in the future and I can continue to build out this list. But do I have to? I don&apos;t think so, as long as those skills remain relevant, I find it incredibly useful to remind myself that &lt;strong&gt;what I&apos;ve done is what I&apos;ll do, and that&apos;s great&lt;/strong&gt;.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Diverge, Converge, and Repeat</title><link>https://andrewwalpole.com/blog/diverge-converge-and-repeat/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/diverge-converge-and-repeat/</guid><description>Divergent and convergent mindsets play a critical role in how I think of ideas and uncover solutions to problems. Here&apos;s a bit about why they&apos;re so useful to me.</description><pubDate>Fri, 13 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Diverge, Converge, and Repeat&lt;/h1&gt;
&lt;p&gt;If I were to roll up the things I&apos;ve learned into high-level concepts, I think one of them might be, &lt;em&gt;mindset&lt;/em&gt;, defined as: the way you intentionally prepare for, and approach, a contextual period of time. What filters do you have at the ready? Are you prepared to be overtly emotional, or defensive or aggressive or curious or passive? Our reactional self, for sure, will have something to say as things happen, but I do believe there is utility in setting your own expectations going into any situation.&lt;/p&gt;
&lt;p&gt;It&apos;s useful to take just a few minutes to think about a situation – it might be a meeting, or a place I&apos;m going to – to think about what my mindset might need to be. What should I be prepared for, and how might I respond to those things?&lt;/p&gt;
&lt;p&gt;There&apos;s a core practice in Design Thinking that is really all about mindset. If you&apos;re unfamiliar with Design Thinking in general, I can&apos;t recommend enough, &lt;a href=&quot;https://thecrazy1.com/episode-33-design-thinking-part-1-overview-and-inspiration-phase/&quot;&gt;Stephen Gates&apos; 3-episode podcast series&lt;/a&gt; on his process of it. Even though I may not carry out the entire process on a regular basis, it is full of concepts that can be broken out into a lot of everyday tasks.&lt;/p&gt;
&lt;p&gt;For this post, I want to focus in quickly on one of those concepts of having a divergent or convergent mindset.&lt;/p&gt;
&lt;h3&gt;Diverge,&lt;/h3&gt;
&lt;p&gt;When you first are looking to generate ideas or tap into creativity, you should put yourself in a divergent mindset. This means that the boundaries – your limits, constraints, tethers – are all not allowed to be a part of the session. You are able to place the problem in an open, safe space and examine it from all possible perspectives that you can think up. The goal is more to explore the landscape of the problem, and uncover many possible paths, even those that may dead-end, than it is to find &lt;em&gt;the&lt;/em&gt; path.&lt;/p&gt;
&lt;h3&gt;Converge,&lt;/h3&gt;
&lt;p&gt;Separate from that divergent, blue-sky session, comes a time to be critical with all you&apos;ve uncovered. Why won&apos;t these things work? What are the risks, efforts, technical debts to take on in this idea? convergent thinking is about focusing a broad blurry vision into a clearer picture. You see the wrinkles and cracks take shape, as well as the beauty and elegance of the ideas that end up being worthy of further effort.&lt;/p&gt;
&lt;h3&gt;and Repeat&lt;/h3&gt;
&lt;p&gt;Separating these two mindsets is invaluable. You may think you can do them together, but they are in direct opposition to each other, and will limit your ability to quickly explore new ideas and possibilities. If you&apos;re having this session with multiple people, it&apos;s also powerful to align on the expectations that everyone adopt this mindset, cutting through the mental red-tape that holds folks back from contributing.&lt;/p&gt;
&lt;p&gt;Finally, &lt;em&gt;repeat the process&lt;/em&gt;. Doing it once may work a bit, but you tend to see the the value in intentionally switching between these mindsets multiple times over many sessions. You will bring new insights to your diverging sessions that you uncovered when you last converged, and vice versa. As you hone in on a solution your sessions may get quicker and you may do less work in each mindset, but you will be left with a direction that you can confidently pursue.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>What&apos;s Your Go-To Web Stack?</title><link>https://andrewwalpole.com/blog/whats-your-go-to-web-stack/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/whats-your-go-to-web-stack/</guid><description>If you had to build a website right now, get started right away, what web technologies are you reaching for?</description><pubDate>Mon, 16 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;What&apos;s Your Go-To Web Stack?&lt;/h1&gt;
&lt;p&gt;One of the best parts of my job is figuring out what technologies a client&apos;s website should be built in. &lt;em&gt;I love the challenge of it&lt;/em&gt;; there are so many different external variables to consider and every client has its own set of opportunities and constraints to align a solution to.&lt;/p&gt;
&lt;p&gt;Out of doing that work, I&apos;ve made it a habit to present the following question to folks, not as any sort of test – though it&apos;s one of my favorite interview questions – but much more so as wanting to fill a genuine curiosity I have.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&apos;s your go-to web stack, right now?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Here&apos;s the set-up:&lt;/h3&gt;
&lt;p&gt;I need you to build me a website right away (of course)! Content on the site will have to be updated at least once a week, but for the most part, the structure won&apos;t change much or at all. I&apos;d love to post a few short, simple blog posts every now and then and I have a contact form I&apos;d like people to reach me through. It&apos;s all designed up, sorry, a templated theme won&apos;t do.&lt;/p&gt;
&lt;p&gt;So what do you do? How do you build this site? What technologies will comprise your stack and workflow? Maybe most importantly, if you aren&apos;t quite sure, what else do you need to know?&lt;/p&gt;
&lt;h3&gt;My Go-To Web Stack&lt;/h3&gt;
&lt;p&gt;For the last year or so, &lt;a href=&quot;https://11ty.dev&quot;&gt;Eleventy&lt;/a&gt; is most likely what I would reach for, though I&apos;m equally familiar with either &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue&lt;/a&gt; + &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; or &lt;a href=&quot;https://nuxtjs.org/&quot;&gt;Nuxt&lt;/a&gt;. I&apos;m at a place where I can get up and building very quickly with Eleventy, and I love that it&apos;s so easy to reach for plugins to add on features, but also introspect those plugins and know quickly that there isn&apos;t too much magic going on behind the scenes. I&apos;ve also gotten accustomed to adding on Vite or &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt; as well to bundle JavaScript and SASS.&lt;/p&gt;
&lt;p&gt;The toughest part would be satisfying the need to regularly update content and post blogs. While Eleventy is great for developers out of the box, not so much for the less tech-inclined. And even though &lt;a href=&quot;/blog/previews-are-hard-also-web-mullets&quot;&gt;previews are hard&lt;/a&gt;, I would likely reach for a headless CMS. I&apos;m torn a bit here, I love &lt;a href=&quot;https://contentful.com&quot;&gt;Contentful&lt;/a&gt;, I used it for years and think it provides a great experience. But the pricing tiers are rough, they&apos;re not positioned for small professional sites that may exceed the free tier. My other option would be &lt;a href=&quot;https://prismic.io&quot;&gt;Prismic&lt;/a&gt;, which really does fit the bill. My only hesitancy there is that I&apos;ve only played with it, and so I have trouble saying it&apos;s part of my go-to stack if I haven&apos;t gone-to it for real yet, but I do think I&apos;m ready to reach for it.&lt;/p&gt;
&lt;p&gt;As for hosting, probably &lt;a href=&quot;https://pages.cloudflare.com/&quot;&gt;Cloudflare pages&lt;/a&gt;, actually, as it has been a great experience with this site and I really love having so many site administration tools at my disposal. Though, it&apos;s just as much of a toss-up to also choose &lt;a href=&quot;https://netlify.com&quot;&gt;Netlify&lt;/a&gt; or &lt;a href=&quot;https://vercel.com&quot;&gt;Vercel&lt;/a&gt;. Netlify especially might win-out because of their easy &lt;a href=&quot;https://www.netlify.com/products/forms/&quot;&gt;forms options&lt;/a&gt;, but I wouldn&apos;t be afraid to custom-roll a simple contact form with a serverless function either.&lt;/p&gt;
&lt;h3&gt;tl;dr&lt;/h3&gt;
&lt;p&gt;I think it&apos;s fascinating to run through this exercise of figuring out what technologies you would actually pick to build a site right at this moment. It&apos;s a bit different from &quot;what do you like?&quot; or &quot;what is ideal?&quot; or &quot;what&apos;s cool?&quot; because it&apos;s much more about understanding where your current comfort zone in web development is, especially because &lt;a href=&quot;/blog/what-youve-done-is-what-youll-do&quot;&gt;what you&apos;ve done is what you&apos;ll do&lt;/a&gt;. Over time, your answer will likely change, so it&apos;s also fun to see what sticks and for how long.&lt;/p&gt;
&lt;p&gt;For me, at this moment, it&apos;s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Eleventy&lt;/li&gt;
&lt;li&gt;SASS and vanilla JS + &lt;a href=&quot;https://github.com/vuejs/petite-vue&quot;&gt;petite-vue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;esbuild&lt;/li&gt;
&lt;li&gt;Prismic&lt;/li&gt;
&lt;li&gt;Cloudflare Pages&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>The Fourth Language</title><link>https://andrewwalpole.com/blog/the-fourth-language/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/the-fourth-language/</guid><description>Have you ever thought about the web having a fourth major, native language? Here are a few of my thoughts on it.</description><pubDate>Thu, 12 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;The Fourth Language&lt;/h1&gt;
&lt;p&gt;The web, and more the browser as we know it today, has its three core technologies that allow us web developers to build web content: HTML, CSS and JavaScript. But what&apos;s next? &lt;em&gt;What will the fourth language be?&lt;/em&gt; I know there&apos;s not much of a dialog to be had here on my blog, though I would love to hear your thoughts, perhaps via &lt;a href=&quot;https://twitter.com/walpolea&quot;&gt;twitter&lt;/a&gt;. But here&apos;s what I think.&lt;/p&gt;
&lt;p&gt;The fourth could be a language of state. There are countless flavors of state-keeping libraries rooted in JavaScript these days, and you might be prepared to argue that they can stay there. There are some really amazing implementations; &lt;a href=&quot;https://github.com/vuejs/core/tree/main/packages/reactivity&quot;&gt;Vue&apos;s reactivity library&lt;/a&gt; being incredibly impressive for how small and simple it is, and so many other examples that have made it easy to build robust and complex application workflows.&lt;/p&gt;
&lt;p&gt;But there&apos;s an issue of interoperability.&lt;/p&gt;
&lt;p&gt;As state libraries deeply ingrain themselves into JavaScript, they fracture the way we are meant to tie together HTML and CSS. Sometimes to the point that the implementor just kicks it all out the door: &lt;strong&gt;JavaScript all the things!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A reactive, state-keeping language is less about bringing new computer science to the web, and more about being a fourth member on the team; its own member, &lt;em&gt;an ambassador of state&lt;/em&gt;, with its own opinions and integrations that could be equal across JavaScript, HTML and CSS. I think it would have an API that each other technology could lean into, to get, update, and react to state changes. When CSS changes the state, HTML reacts. When JavaScript changes the state, CSS and HTML react; all equally delegated to and updated from this new friend.&lt;/p&gt;
&lt;p&gt;We already do these things, but the wires are left exposed and tangled, woven throughout these three existing layers, causing concession and work-around. A fourth language, a fourth layer, to abstract away the mess into a place that there is no mess at all – like a perfectly wired server rack – would remove a burden from both the existing tech we shoe-horn this functionality into and the developer who needs to manage and orchestrate it all (especially over time).&lt;/p&gt;
&lt;p&gt;I think it could be JSON-ish, and even have some amount of compatibility there, but it would have some syntactic flair of its own for sure. An elegant exposure of models and relationships between data, not unlike what you see in &lt;a href=&quot;https://xstate.js.org/&quot;&gt;XState&lt;/a&gt;. A way to focus solely on describing how your website or app collectively quantifies the inputs, outputs and triggered sequences of action and change that need to be known and possibly occur.&lt;/p&gt;
&lt;p&gt;So that&apos;s what I think, and I have an appetite for it too. Sure, &lt;em&gt;it&apos;s another thing&lt;/em&gt; but that&apos;s not a foreign concept for us web-folk. I&apos;d much rather it be so extra super new that we can&apos;t ignore it; that we can&apos;t just throw it out and move on when we get bored. Instead, we can rally around it, and grow an entirely new community of folks that can help the rest of us through it, and we can clean up our code, and make room for a bunch of new crazy-yet-terrible innovation on the web.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Building a Like Button with Cloudflare Workers</title><link>https://andrewwalpole.com/blog/building-a-like-button-with-cloudflare-workers/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/building-a-like-button-with-cloudflare-workers/</guid><description>Cloudflare Workers (Edge Functions) and their specialized KV product are the perfect tool to create a quick little API with persistent state.</description><pubDate>Fri, 06 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Building a Like Button with Cloudflare Workers&lt;/h1&gt;
&lt;p&gt;Previously, I &lt;a href=&quot;/blog/living-on-the-edge-functions&quot;&gt;touched lightly on Cloudflare Workers&lt;/a&gt;. In this post, we&apos;ll get deeper into the weeds and see how we can use them to build a small portable like button (exactly the one you see down in the bottom right of the page – sorry RSS folks).&lt;/p&gt;
&lt;h2&gt;What are Cloudflare Workers and Cloudflare KV?&lt;/h2&gt;
&lt;p&gt;Cloudflare workers are edge functions (serverless functions that run on an edge server, so they&apos;re super duper fast). &lt;em&gt;They&apos;re just a bit of Javascript that can do anything Javascripty you want and return a value, like some json or even an entire webpage&lt;/em&gt;. By default, when you create one, you get a url that you can curl/fetch/browse-to that will trigger the function to run. You can also disable that url and point specific routes of your website to trigger the function when they are navigated to.&lt;/p&gt;
&lt;p&gt;Cloudflare KV is a separate-but-related offering from Cloudflare. The KV stands for Key/Value, and it&apos;s essentially a flat, persistent, key/value store that Cloudflare can make available to your Cloudflare workers. &lt;em&gt;This is sort of a big deal&lt;/em&gt;. There are some great serverless function offerings out there, but none make it quite so easy to bolt on a small, fast, synchronized &lt;strong&gt;database&lt;/strong&gt; as Cloudflare has (Firebase comes close, but if you know of any others, I would love to hear about them).&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;There&apos;s a small bit to get set up before you can use workers and KV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You need a &lt;a href=&quot;https://dash.cloudflare.com/sign-up/workers&quot;&gt;cloudflare account&lt;/a&gt; (it&apos;s free!)&lt;/li&gt;
&lt;li&gt;The sign up process wil set you up with a free &lt;code&gt;*.workers.dev&lt;/code&gt; sub-domain, which is where all of your worker urls will live, if you are using an existing Cloudflare account, you can set that up in the workers section of the dashboard.&lt;/li&gt;
&lt;li&gt;Once you have that set up, you&apos;re ready to create and use workers – up to 100,000 runs per day for the free tier!&lt;/li&gt;
&lt;li&gt;You can also follow the &lt;a href=&quot;https://developers.cloudflare.com/workers/get-started/guide/&quot;&gt;getting started guide&lt;/a&gt;, which will walk you through setting up the Workers CLI so you can develop and publish workers from your local machine. In this tutorial, we&apos;re skipping that and doing everything (even writing the code!) on the Cloudflare dashboard.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Create the worker&lt;/h2&gt;
&lt;p&gt;Under the workers tab of your Cloudflare account dashboard is where you can create workers and manage KV. In there, click the, &lt;code&gt;Create a Service&lt;/code&gt; button. If you don&apos;t see that button, you may still need to set up your &lt;code&gt;workers.dev&lt;/code&gt; sub-domain; follow the steps.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Give your service a name, I&apos;ll call mine, &lt;code&gt;likes&lt;/code&gt;. The name dictates the function endpoint, so in this case it will be &lt;code&gt;https://likes.walpolea.workers.dev&lt;/code&gt;. Then choose a starter, I&apos;ll go with the bare &lt;code&gt;HTTP Handler&lt;/code&gt; which will provide a small boilerplate function to get going with. Hit &lt;code&gt;Create Service&lt;/code&gt; and your worker is ready for use!&lt;/p&gt;
&lt;h2&gt;Create the KV&lt;/h2&gt;
&lt;p&gt;On the dashboard menu, under Workers, choose, &lt;code&gt;KV&lt;/code&gt; and click the &lt;code&gt;Create Namespace&lt;/code&gt; button. Give the namespace a name, in this case, &lt;code&gt;likes&lt;/code&gt; and click, &lt;code&gt;Add&lt;/code&gt;. You now have a KV store set up, you can use the dashboard to see the values and even update them manually if you need to.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Hook KV up to the Worker&lt;/h2&gt;
&lt;p&gt;One more important step to get KV working with your worker. Go into your Workers Overview, click the &lt;code&gt;likes&lt;/code&gt; worker. In the detailed worker dashboard, go to &lt;code&gt;Settings&lt;/code&gt; and then the &lt;code&gt;Variables&lt;/code&gt; tab. Theres a section at the bottom called &lt;code&gt;KV Namespace Bindings&lt;/code&gt;. You need to provide a variable name, &lt;code&gt;LIKES&lt;/code&gt; and connect it to your &lt;code&gt;likes&lt;/code&gt; KV Namespace.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;Write the worker code&lt;/h2&gt;
&lt;p&gt;We&apos;re going to write the worker directly on Cloudflare. On the worker details dashboard you were just on, click the, &lt;code&gt;Quick Edit&lt;/code&gt; button. I could tediously walk you through building the code line-by-line, but I would rather just show it all to you. If you have some base knowledge of serverless functions it should be pretty straight-forward. So here it is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//this is the very first thing to run, a fetch event happened
//it calls handleRequest which needs to figure out how to
//respond back to the client
addEventListener(&quot;fetch&quot;, event =&amp;gt; {
  event.respondWith(handleRequest(event.request))
});

async function handleRequest(request) {
  
  //This function will handle both getting (Get) and setting (Post) our likes
  //Use request.method to check which it is.
  switch(request.method) {

    //This is required because our preflight checks
    //need cors enabled
    case &quot;OPTIONS&quot;:
      return responseJSON({ok: &quot;OK&quot;});
    break;

    //This is where we look up and return the likes value
    //based on the key (title) passed in as a url param
    case &quot;GET&quot;:

      const { search } = new URL(request.url);
      const params = new URLSearchParams(search);

      if( params.has(&apos;title&apos;) ) {
        const title = params.get(&apos;title&apos;);
        let likes = await getLikes(title); //see below
        likes = likes ? likes : 0; //if it&apos;s null, it&apos;s 0

        return responseJSON({title, likes});
      }

    break;

    //This is where we increment the number of likes
    //based on the key (title) which is passed in as json data
    case &quot;POST&quot;:

      const { title } = await request.json();
      let likes = await incrementLikes(title); //see below

      return responseJSON({ title, likes });
      
    break;
  }

  //a catch-all response if something weird happens
  return responseJSON({error:&quot;Something went wrong&quot;});
}

//returns a json response object with data as the payload
function responseJSON( data ) {
  return new Response(JSON.stringify(data),{

    //this is the secret to enableing cors
    //feel free to set the origin to a value that you trust
    const corsHeaders = {
      &apos;Access-Control-Allow-Origin&apos;: &apos;*&apos;,
      &apos;Access-Control-Allow-Headers&apos;: &apos;*&apos;,
      &apos;Access-Control-Allow-Methods&apos;: &apos;GET,HEAD,POST,OPTIONS&apos;,
    };

    headers: {
      ...corsHeaders,
      &apos;content-type&apos;: &apos;application/json;charset=UTF-8&apos;,
    },
  });
}

//This function retrieves the number of likes
//in the KV based on the key (title)
async function getLikes( title ) {
  //get the value from KV
  const likes = parseInt( await LIKES.get(title, { type: &quot;text&quot;}));
  return likes;
}

//This function increments the number of likes
//in the KV based on the key (title)
async function incrementLikes( title ) {
  let likes = await getLikes(title);
  
  if( likes ) { //does this have a value, or is it null?
    likes++;
  } else { //the first time it&apos;s liked set it to 1
    likes = 1;
  }

  //update the value in KV
  await LIKES.put( title, likes.toString() );

  return likes;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The worker is set up to handle two types of requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GET&lt;/code&gt; with a &lt;code&gt;?title=SOME-KEY&lt;/code&gt; parameter, which will return the number of likes stored in that key.&lt;/li&gt;
&lt;li&gt;and &lt;code&gt;POST&lt;/code&gt; with a json payload of &lt;code&gt;{ title: &apos;SOME-KEY&apos; }&lt;/code&gt; which will increment the number of likes at that key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You absolutely could split these out into two separate workers, but this seemed easier to show off. If you get stuck, I have to shoutout the &lt;a href=&quot;https://developers.cloudflare.com/workers/learning/&quot;&gt;Cloudflare docs&lt;/a&gt;, they are great and have &lt;a href=&quot;https://developers.cloudflare.com/workers/examples/&quot;&gt;a large variety of examples&lt;/a&gt; that show you exactly what code you need.&lt;/p&gt;
&lt;h2&gt;Hooking up the front-end&lt;/h2&gt;
&lt;p&gt;Once again, I&apos;m opting to keep it short here and just show you. You can pretty easily start using your new likes API with a couple of fetch commands. Here&apos;s a pen showing the like button in action:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;580&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;QWQbgjV&quot; data-user=&quot;walpolea&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/walpolea/pen/QWQbgjV&quot;&amp;gt;
Petite-vue Like Button powered by Cloudflare Workers and KV&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;The secret sauce is really in these two functions that hit the worker we built:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const endpoint = &apos;https://likes.walpolea.workers.dev/&apos;;

async function loadLikes( title ) {
  return (await ( await fetch(`${endpoint}?title=${title}`)).json()).likes;
}

async function postLike( title ) {
  if( !this.likeAdded ) {
    const response = await ( await fetch(endpoint, {
      method: &apos;POST&apos;,
      headers: {
        &apos;Accept&apos;: &apos;application/json&apos;,
        &apos;Content-Type&apos;: &apos;application/json&apos;
      },
      body: JSON.stringify({title})
    })).json();

    this.likeAdded = true;
    return response.likes;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Wrap-up&lt;/h2&gt;
&lt;p&gt;If you made it this far, &lt;em&gt;whew&lt;/em&gt;, thanks for joining me on this journey. Feel free to actually like this post down in the bottom right (sorry RSS folks). I hope you can see how easy and powerful Cloudflare workers and KV are. You could use them in so many ways beyond this simple example.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Stop Leading Zeros with Leximited Notation</title><link>https://andrewwalpole.com/blog/stop-leading-zeros-with-leximited-notation/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/stop-leading-zeros-with-leximited-notation/</guid><description>Ever hit the issue where you want numbered items to alphabetically order themselves numerically, so you lead them with zeros, which works until you have more digits than zeros. Leximited notation solves this issue, and then some!</description><pubDate>Tue, 10 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Stop Leading Zeros with Leximited Notation&lt;/h1&gt;
&lt;p&gt;Have you ever started a content series of some kind – maybe videos, music tracks, book chapters, blog posts or podcast episodes – where you want the episode number to be a part of the title, and so you just start naming them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;​1. Introduction&lt;/li&gt;
&lt;li&gt;​2. The thing after the introduction&lt;/li&gt;
&lt;li&gt;​3. We&apos;re well beyond the intro now&lt;/li&gt;
&lt;li&gt;.&lt;/li&gt;
&lt;li&gt;.&lt;/li&gt;
&lt;li&gt;.&lt;/li&gt;
&lt;li&gt;​10. Quite a milestone&lt;/li&gt;
&lt;li&gt;​11. It goes to to eleven&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Have you seen the problem yet? You can sort numbers easy-enough, sure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21].sort()

OUTPUT: [1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 3, 4, 5, 6, 7, 8, 9]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Whoops!&lt;/em&gt; Oh yeah, it&apos;s JavaScript, you can sort numbers &lt;s&gt;easy-enough&lt;/s&gt;, sure:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21].sort( (a,b) =&amp;gt; a - b )

OUTPUT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you saw in the first example, on the default &lt;code&gt;Array.sort()&lt;/code&gt;, even numerical values in JavaScript undergo an alphabetical sort, and it&apos;s no different when those numbers are strings. It&apos;s even harder as part of a title to cook-up a function that is going to both parse out the number from many characters and attempt to sort it correctly. Beyond that, our content lives in databases and across platforms, and so the issue gets worse as you may not even be in control of the sort algorithm.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Did you know that the non-numerical ordering seen above has a name? It&apos;s lexicographical ordering, or lexical order. Remember that now!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h3&gt;Enter: Leading Zeros&lt;/h3&gt;
&lt;p&gt;I&apos;ll be brief here, because, &lt;em&gt;spoiler alert&lt;/em&gt;, this is not the answer I&apos;m recommending. You can trade that sorting issue for another. You can lead the numbers with zeros. From an alphabetical perspective, your sorting will now check out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[&apos;001&apos;, &apos;002&apos;, &apos;003&apos;, ... &apos;010&apos;, &apos;011&apos;].sort();

OUTPUT: [&apos;001&apos;, &apos;002&apos;, &apos;003&apos;, ... &apos;010&apos;, &apos;011&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But do you see the issue you traded in for? Up front, you need to commit to a number of leading zeros, and as soon as you hit a number that extends beyond the bounds of your zeros, you&apos;re right back to the original sorting mess.&lt;/p&gt;
&lt;h3&gt;A new challenger approaches: Leximited Notation&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/livcomp&quot;&gt;Dave Ackley&lt;/a&gt; came up with a different style of numerical notation that solves both issues. A notation that &lt;em&gt;when sorted alphabetically or numerically maintains the same order&lt;/em&gt;: &lt;a href=&quot;https://github.com/elenasa/ULAM/wiki/Appendix-D%3A-Leximited-Format&quot;&gt;Leximited Notation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&apos;s pretty easy to learn, kind of like pig-latin as a kid, it&apos;s a bit odd at first, but then you get the hang of it quickly as you go. Here&apos;s how it works:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You prepend the number&apos;s character length to the number itself.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;That&apos;s it!&lt;/strong&gt; Let&apos;s take a look at some examples:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;4 has a character length of 1, so in Leximited notation it&apos;s: 14&lt;/code&gt; The length is prepended to the number itself.&lt;/p&gt;
&lt;p&gt;Another: &lt;code&gt;37 has a character length of 2, so in Leximited notation it&apos;s: 237&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s try counting!&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ... 99, 100, 101&lt;/code&gt; in Leximited format would be: &lt;code&gt;10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 210, 211, 212, 213, ... 299, 3100, 3101&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The numbers are also self-delimiting which means if you parse a string with leximited numbers, you can use the first value to know how many character spaces to parse out of the string to get the entire number. So, to complete the &lt;em&gt;&quot;Ahhhhhh, I get it!&quot;&lt;/em&gt; moment, the name comes from:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Lex&lt;/em&gt;icographically Del&lt;em&gt;imited&lt;/em&gt; Notation!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ahhhhhh, I get it!&lt;/p&gt;
&lt;h3&gt;tl;dr&lt;/h3&gt;
&lt;p&gt;Leximited Notation solves an annoying issue where numbers as strings, especially in titles of things, lexicographically sort differently from numerical sorting. You can lead those numbers with zeros, but that&apos;s both ugly and flawed. Leximited notation on the other hand is easy: &lt;em&gt;You just prepend the number&apos;s character length to the number itself&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You can &lt;a href=&quot;https://github.com/elenasa/ULAM/wiki/Appendix-D%3A-Leximited-Format&quot;&gt;read all the juicy details on Leximited Notation&lt;/a&gt; or if you want to use it in code, I put together a simple &lt;a href=&quot;https://github.com/walpolea/leximitedjs&quot;&gt;JavaScript encoder/decoder&lt;/a&gt; on github.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Work is a Place to Help People</title><link>https://andrewwalpole.com/blog/work-is-a-place-to-help-people/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/work-is-a-place-to-help-people/</guid><description>Reframing how you think about work toward a perspective of helping the folks around you can spread positive change at multiple levels.</description><pubDate>Fri, 20 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Work is a Place to Help People&lt;/h1&gt;
&lt;p&gt;The angle at which you approach all things is important. That&apos;s certainly one of my big recurring threads. It&apos;s a broad enough idea that it shows up all over the place, &lt;em&gt;&quot;look on the bright side,&quot;&lt;/em&gt; &lt;em&gt;&quot;the glass is half-full,&quot;&lt;/em&gt; &lt;em&gt;&quot;take a walk in their shoes;&quot;&lt;/em&gt; all of these notions are concerned about the way we interpret, and then react to, the world in front of us.&lt;/p&gt;
&lt;p&gt;I like to carry this idea with me as often as I can. One of those places I carry it to is my work. I can&apos;t really say when or where or from who the idea came from, but at some point in my career – I think around the time I started leading people – I made a fundamental shift in how I perceive work.&lt;/p&gt;
&lt;p&gt;Work used to be a place to contribute meaningfully to a business, and in return be provided a snug-fitting piece of the life-puzzle. It certainly isn&apos;t not that, just as the glass may also actually be half-empty. But I shifted to an idea that has really made how I approach work a lot more fulfilling.&lt;/p&gt;
&lt;p&gt;Work, to me now, is a place to help people. I&apos;m not just talking about what your company does for society, though certainly, many businesses are in the business of helping people at all sorts of levels, and that&apos;s not to be overlooked. Really though, I&apos;m mostly talking about your co-workers. You&apos;re plopped into the lives of these people; a bunch of would-be strangers, connected for five days a week, many hours a day:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Help them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because, why not? &lt;em&gt;Be helpful.&lt;/em&gt; Adopt a helping attitude. You don&apos;t have to be all sunshine and roses all the time, certainly therapists aren&apos;t and they help people, sometimes in the biggest ways. You have a job to do, sure, do that, but &lt;em&gt;fight for the right&lt;/em&gt; to have working-day capacity to help the people around you, it&apos;s important.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What do you need from me?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What are your biggest work worries?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What&apos;s the hardest thing about working here?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Is there anything I can do to help you?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This attitude, perception, mindset, these questions, are some of the most important to me as a leader and as a contributor at work. Sometimes work is stressful and busy and hard for me too, and maybe I&apos;m less helpful then; hopefully someone offers me help, we all could use some sometimes.&lt;/p&gt;
&lt;p&gt;Even the business strategy side of me likes this idea of helping people. It&apos;s pretty dang rock-solid. You will shape the working culture, your leaders will notice, people will like that you&apos;re helpful, folks will follow your lead, you will leave a more-than-usual impression on their lives, you will feel more fulfilled helping people than you will doing your day-to-day job. And I believe you can count on this idea, doesn&apos;t matter if you&apos;re a part-time fry cook, or the CTO of a big tech company, or anything in-between. &lt;strong&gt;People want to work in a place where people are helpful. So do that, &lt;em&gt;that&apos;s the work&lt;/em&gt;.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Do it for Context</title><link>https://andrewwalpole.com/blog/do-it-for-context/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/do-it-for-context/</guid><description>Sometimes you need that little extra spark to go down a new pathway or face the unknown. When you&apos;re grasping for that push, do it for context.</description><pubDate>Tue, 31 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Do it for Context&lt;/h1&gt;
&lt;p&gt;In 2019 I made a ten episode podcast series called &lt;a href=&quot;https://anchor.fm/codesnippet&quot;&gt;Code Snippet&lt;/a&gt;. The idea of doing a podcast had been rattling around in my brain for many years prior, but I just didn&apos;t have the ingredients that pushed me to take action on it. A few lukewarm ideas, no one to co-host with, mediocre equipment. But those were just excuses, not real reasons why I couldn&apos;t make a podcast. Finally, the thing that got me to do it was this simple idea:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Do it for context.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;Context informs the shape of the lens with which we perceive things&lt;/em&gt;; it is the key to understanding with confidence.&lt;/p&gt;
&lt;p&gt;I had no further expectations other than to learn how to make a podcast, and in hindsight, this was an amazingly wonderful reason to get started.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do I publish a podcast?&lt;/li&gt;
&lt;li&gt;Do I need a script?&lt;/li&gt;
&lt;li&gt;How do I come up with topics?&lt;/li&gt;
&lt;li&gt;How should episodes be structured?&lt;/li&gt;
&lt;li&gt;Do I even sound good?&lt;/li&gt;
&lt;li&gt;How much editing is involved?&lt;/li&gt;
&lt;li&gt;Will people listen?&lt;/li&gt;
&lt;li&gt;What else am I missing?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these things had to be answered in a practical manner, so instead of leaving it to theoretical punditry to solve each question, the solutions had to be accessible, and easy enough to fit into a reasonable amount of effort. On top of that, I had chosen to ignore two issues that had previously held me back:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I dislike most podcasts with a single host&lt;/li&gt;
&lt;li&gt;and mouth-blogging code is awkward and hard to follow along with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since it was just me, and the topic I had chosen had exactly these two characteristics, I plowed ahead in spite of my extreme hesitation. And I did it! I used anchor.fm to do the heavy lifting for publishing my podcast, Adobe Audition for editing, learned that I really need to work on my &lt;em&gt;umms&lt;/em&gt; and &lt;em&gt;uhhs&lt;/em&gt;, but otherwise, I gained an entirely new outlook on what making podcasts was all about.&lt;/p&gt;
&lt;p&gt;In the end, I decided that my goal was accomplished and the content wasn&apos;t worth keeping going. But I got a more-than-expected amount of listens and gained a wealth of knowledge and understanding.&lt;/p&gt;
&lt;p&gt;And so, the larger takeaway for me was really that some things are just worth doing for the experience of it; for the understanding of the little details that can easily go unseen until you&apos;re in the thick of it. So the next time you&apos;re unsure about getting started, or allowing excuses to stop you, go into it with a simple goal: To gain understanding; &lt;em&gt;for context&lt;/em&gt;.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/do-it-for-context.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Accessibility is Hard and Important</title><link>https://andrewwalpole.com/blog/accessibility-is-hard-and-important/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/accessibility-is-hard-and-important/</guid><description>Two things about web accessibility that we need to stop letting hold us back.</description><pubDate>Thu, 19 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Accessibility is Hard and Important&lt;/h1&gt;
&lt;p&gt;Were you aware that today, May 19th, is &lt;a href=&quot;https://accessibility.day/&quot;&gt;Global Accessibility Awareness Day&lt;/a&gt;? How do you feel about that?&lt;/p&gt;
&lt;p&gt;For a lot of web developers, including myself, the feeling can often be somewhere along the spectrum of shame and despondency. Why? Because &lt;em&gt;Accessibility is hard and important&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you feel this way, you&apos;re not alone.&lt;/strong&gt; We have to acknowledge these feelings, face them head on.&lt;/p&gt;
&lt;h3&gt;It&apos;s Important&lt;/h3&gt;
&lt;p&gt;We feel shame because it&apos;s important to us. We care, and that&apos;s a good starting point. I feel ashamed when I write inaccessible HTML because there might be someone out there who I&apos;m frustrating, or letting down because they can&apos;t access the content the same way a non-disabled person might.&lt;/p&gt;
&lt;p&gt;It&apos;s not comfortable, but I&apos;m telling you here and now that it&apos;s important enough to not run away from. Embrace the discomfort, &lt;em&gt;embrace the shame&lt;/em&gt;, take a step in a better direction. Even if it&apos;s two steps forward and one step back, it&apos;s important to keep at it.&lt;/p&gt;
&lt;h3&gt;It&apos;s hard&lt;/h3&gt;
&lt;p&gt;Writing accessible HTML is hard for a bunch of reasons, I won&apos;t dive deep, but I will say that I think the biggest reasons are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;As seasoned developers, we&apos;re too prideful to admit and/or unwilling to be told that the skills we&apos;ve worked so hard to hone in this industry are no longer good enough.&lt;/li&gt;
&lt;li&gt;Curricula for new-comers to the industry is not adapting quick enough to address the accessibility missteps in our web development culture.&lt;/li&gt;
&lt;li&gt;There is not enough accountability enforced at the top levels of for-profit companies to allow accessibility a proper seat at the operational table.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Some of those are big things. Sure let&apos;s keep an eye on them, but accessibility is hard, let&apos;s now move on from that idea, it&apos;s something to overcome, not an excuse to hold you back. &lt;em&gt;Figure out your next step.&lt;/em&gt; What HTML did you write this week? Go open it up and take a look, I&apos;ll wait...&lt;/p&gt;
&lt;p&gt;Is it written semantically? Are you applying WAI-ARIA standards? Is the site navigable via the keyboard? &lt;em&gt;It&apos;s a lot, I get it&lt;/em&gt;, those aren&apos;t even all of the questions to ask, but pick one, and get to work, it&apos;s hard and important.&lt;/p&gt;
&lt;p&gt;I&apos;ll be right there with you, a self-proclaimed expert, a seasoned web developer, learning a bunch of new things, unlearning a bunch of bad habits. Let&apos;s do it, it&apos;s hard and important.&lt;/p&gt;
&lt;h3&gt;Take a step&lt;/h3&gt;
&lt;p&gt;Not sure where to go next? Check out these resources I picked out that distill down some of the focus areas. &lt;em&gt;Pick one and just start reading.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.accessibility-developer-guide.com/&quot;&gt;Accessibility Developer Guide&lt;/a&gt; - especially check out the examples section!&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://moritzgiessmann.de/accessibility-cheatsheet/&quot;&gt;Accessibility Cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.digitala11y.com/wai-aria-1-1-cheat-sheet/&quot;&gt;WAI-ARIA Cheatsheet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://romeo.elsevier.com/accessibility_checklist/&quot;&gt;W3C Accessibility Checklist&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://w3c.github.io/aria-practices/&quot;&gt;W3C WAI-ARIA Authoring Practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Do Hard Things Again</title><link>https://andrewwalpole.com/blog/do-hard-things-again/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/do-hard-things-again/</guid><description>It feels really great to get over a hard hurdle. But don&apos;t stop there, do it again.</description><pubDate>Tue, 14 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Do Hard Things Again&lt;/h1&gt;
&lt;p&gt;One of my longer-lived side projects has been &lt;a href=&quot;https://mfm.rocks&quot;&gt;mfm.rocks&lt;/a&gt;. And while the explanation for what&apos;s going on there deserves its own post, let&apos;s save that for another day.&lt;/p&gt;
&lt;p&gt;I was recently doing some development on what is now the 2.0 version of the platform there, and for a bit I had been putting off reimplementing a particular element called &lt;code&gt;SwapWorm&lt;/code&gt;, that I severely struggled through putting together on version 1.0.&lt;/p&gt;
&lt;p&gt;Scared isn&apos;t quite the the right feeling I had about it; perhaps dread is slightly more fitting. I vividly remember fighting nasty bugs with &lt;code&gt;SwapWorm&lt;/code&gt; for weeks, unsure why it wasn&apos;t quite doing what I needed it to do. Ultimately, I did find the issue and conquer the bugs, and it felt really great to finally overcome the challenge.&lt;/p&gt;
&lt;p&gt;Well, I dove in on it a few days ago, and what a different experience it was. &lt;a href=&quot;https://mfm.rocks/?size=128,64&amp;amp;speed=1&amp;amp;selected=SWAPWORM&amp;amp;atoms=SWAPWORM-42x22-53x38-60x16-71x38-80x25&quot;&gt;&lt;code&gt;SwapWorm 2.0&lt;/code&gt;&lt;/a&gt; came together in an hour or two, and sure there were bugs to work through, but nothing like it had been before. I was left a bit stunned. The code flowed, it all seemed so much more simple, I had clarity for the task at hand, and so much more experience programming in this type of environment since the first go-around.&lt;/p&gt;
&lt;p&gt;And so, that&apos;s this post&apos;s message. It&apos;s great to tackle a hard problem and overcome it. But also &lt;em&gt;make a point to tackle the hard problems again&lt;/em&gt;, especially the ones that really stick in your mind. I think you&apos;ll find, as I did, that it will be an entirely new experience that can show you how much you&apos;ve progressed and reward you with a new kind of satisfaction.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/do-hard-things-again.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Escape Hatches</title><link>https://andrewwalpole.com/blog/escape-hatches/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/escape-hatches/</guid><description>Third-party frameworks and systems abstract away complexity, but often at the cost of flexibility. Escape hatches can be a great way to bring flexibility back without compromising features.</description><pubDate>Fri, 17 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Escape Hatches&lt;/h1&gt;
&lt;p&gt;In web development, we rely on a lot of 3rd party systems when it comes to the practical building of websites and apps. &lt;em&gt;Your stack is a conglomeration of important decisions.&lt;/em&gt; As someone responsible for making those decisions, the choice often comes down to:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What do I get, and what do I give up?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each framework, technology, or system gives you something – proprietary features, enhanced development experiences, hyper-specific workflows; essentially abstractions and shortcuts to getting the work you need to do done quicker and easier. You&apos;re buying-in to system productivity.&lt;/p&gt;
&lt;p&gt;At the same time, all of those abstractions force you into a specific set of constraints. Some systems only integrate well with particular other systems. Others, may only solve for specific use-cases on top of a general process, cutting down on flexibility (or maybe it makes you feel better to call this complexity). Wordpress, as a classic example, has features that require specific technologies to sit around it, dictating the full stack – PHP, MySQL, Apache/nginx – but in return you get quick entry into building front-end pages, managing content, and a whole host of functions that tackle non-trivial common web-based situations.&lt;/p&gt;
&lt;p&gt;Anyway, not to belabor the point:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Abstractions have trade-offs that require careful consideration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What I really want to say is, maybe they don&apos;t always have to. Certainly, some levels of the stack would have a harder time than others at this, but providing escape hatches to eject out of the abstraction, down into the underlying technology, can do wonders for a product&apos;s value. As an architect, I have learned to put &lt;em&gt;heavy weight&lt;/em&gt; on technologies that provide both the happy-path as well as the ability to deviate when critical opportunities or edge-cases arise.&lt;/p&gt;
&lt;p&gt;A great example of this is Vite. I have to think that a part of its popularity is not just the happy-path it provides, but also the ability to exit down into both the raw rollup and esbuild configurations when you need to; a feature I&apos;ve already used more than a handful of times in production systems.&lt;/p&gt;
&lt;h3&gt;Why don&apos;t more companies provide escape hatches?&lt;/h3&gt;
&lt;p&gt;I think it&apos;s two things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There is a common mindset that the happy-path, your product experience, &lt;em&gt;defines your product&lt;/em&gt;, and there just isn&apos;t enough internal perception to look beyond those walls. It&apos;s rarely questioned, and perhaps when it is, it&apos;s hard to argue away resources from focusing on bringing direct value to your offering.&lt;/li&gt;
&lt;li&gt;Related, I think it&apos;s also common to think that if you expose the escape hatch, you also need to start paving the road out there too. It becomes hard to let go, it may even feel like if a whole portion of the experience you provide has no support, you&apos;re letting your users down in some way.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I think these reasons are both short-sighted, and are mostly a product of not considering a different audience: the folks that take on the debt of your product. I don&apos;t care if the escape hatch leads to treacherous territory, I agree to the terms and welcome the ability to not be hamstrung when executing on my very specific business needs.&lt;/p&gt;
&lt;p&gt;So, if you have a hand in building your product, take a moment to consider the escape hatch; a way to leverage the features you&apos;re already building on top of, and a way to provide welcomed value to those who seek to push the limits of your happy-path.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/escape-hatches.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Build Momentum for the Next Thing</title><link>https://andrewwalpole.com/blog/build-momentum-for-the-next-thing/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/build-momentum-for-the-next-thing/</guid><description>A lot of focus remains on success and failure as traits of progress, but I&apos;ve learned that it&apos;s really momentum that you should keep your eye on.</description><pubDate>Wed, 13 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Build Momentum for the Next Thing&lt;/h1&gt;
&lt;p&gt;It&apos;s very easy to get stuck in a mindset of worrying about whether what you are doing will be successful or a failure. It makes a lot of sense, because often how we get to perceive the progress of others is through their milestones (the things deemed successful or failures), and not their full nuanced progression.&lt;/p&gt;
&lt;p&gt;My take though, is that &lt;em&gt;success and failure are only by-products&lt;/em&gt; of a greater trait that is often overlooked; momentum!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&apos;s always in the last place you look&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s a groaner for sure, but I&apos;ve always thought it interesting how a silly quip can be so truth-rooted. I think the same goes for momentum:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Doing something always leads to the next thing&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Meaning, just get started, &lt;em&gt;do something&lt;/em&gt;, start the wheels turning. It&apos;s advice you hear again and again, it&apos;s advice I still often need to hear, but I will say that it&apos;s also a core principle that, when I am able to keep mindful of, has been an invaluable notion in all aspects of my life.&lt;/p&gt;
&lt;p&gt;Big, hard goals and tasks take momentum, and so that&apos;s the punch-line here, &lt;em&gt;focus on the momentum&lt;/em&gt;, focus on the getting-going. Step, then step again, and eventually, as winding of a path it may end up being you will get there. But in contrast, stagnation over time can stick you in the mud, and the advice there is, &lt;em&gt;acknowledge the mud&lt;/em&gt;, understand that it will be a slog to getting-going, and try not to let it discourage you and stagnate you more.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/build-momentum-for-the-next-thing.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>We Need Context Management Systems</title><link>https://andrewwalpole.com/blog/we-need-context-management-systems/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/we-need-context-management-systems/</guid><description>Applying context to websites often lives tied to content, or tied to the front end, or split into both. Here I propose building a new contextual layer separate from both.</description><pubDate>Fri, 18 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;We Need Context Management Systems&lt;/h1&gt;
&lt;p&gt;The problem with headless content management systems is that they don&apos;t cleanly account for the context of content to always be defined in a proper place.&lt;/p&gt;
&lt;p&gt;Take a headless CMS with content representing a restaurant menu. In that instance, the content can be modeled in a way that is very easy to manage without assigning context to it. A very clean separation where content manager can happily keep up the data while the front end assumes the role of applying that content into its final context.&lt;/p&gt;
&lt;p&gt;However, take a homepage, comprised of components (or blocks or modules). Here we have the content – the words and media on the page – as well as a specific context – the order and layout decisions that mold content into a consumable digital experience.&lt;/p&gt;
&lt;p&gt;Just ordering the list of components on the screen is applying a hierarchical context to the page. And then each component itself is often riddled with specific context choices. A hero: maybe the sizing and position of the heading text relative to a background image laid underneath. A grid of product cards might also give you control over the amount of columns or choosing a special layout for the first item to take on.&lt;/p&gt;
&lt;p&gt;Abandoning a hard-lined rule that the CMS contains content and website code applies context comes with many trade-offs and implications.&lt;/p&gt;
&lt;h2&gt;Mixing content and context can be good&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The content manager now wields more control over the website: this can lead to better upkeep and longevity in the utility of the site.&lt;/li&gt;
&lt;li&gt;It can make business sense: the previously mentioned longevity is attributed to a more cost-effective management of a site, as well as being able to streamline experiential changes to match the evolving goals of the business.&lt;/li&gt;
&lt;li&gt;Deciding on context first, when your content is still taking shape, can help solidify your content choices given the context limitations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;But tight coupling leads to a lot of bad&lt;/h2&gt;
&lt;p&gt;Those benefits trade-off with things like a much heavier up-front cost to develop contextual flexibility into the site.&lt;/p&gt;
&lt;p&gt;Also tying large amounts of context into a headless CMS erodes away a major benefit of headless in the first place: your contextual content now becomes specifically dependent on your component API, making it much harder to move away from that exact front-end implementation.&lt;/p&gt;
&lt;p&gt;Without a clear line established, the decision for where every contextual variant in a component now becomes a discussion: How much configuration is appropriate for this component? Where will this get configured, and how? What&apos;s the experience of setting, evaluating and committing to contextual configurations?&lt;/p&gt;
&lt;p&gt;The answering of these questions equates to significant time and effort for building a website. Furthermore, the decisions are often arbitrary without an underlying rule to strengthen them. This leaves a fragile barrier that invites continuous debate and last-minute changes as or after a system is built.&lt;/p&gt;
&lt;h2&gt;It&apos;s hard to separate content and context&lt;/h2&gt;
&lt;p&gt;All that said, it might seem simple: stop mixing context and content! But that&apos;s actually hard. Context and content inform each other. As frustrating as it may be on the development side to manage a control panel of contextual inputs, it&apos;s also maddening for a content creator to build what they need to without a visual guide of what the end experience will be.&lt;/p&gt;
&lt;h2&gt;But it can be done&lt;/h2&gt;
&lt;p&gt;I think there are two solutions to lay out here:&lt;/p&gt;
&lt;h3&gt;Consider context strategically.&lt;/h3&gt;
&lt;p&gt;The first is taking on strategic consideration of the technical implementation of handling context as part of defining the project&apos;s scope. Become conscious of where contextual application sits in the thing you&apos;re building.&lt;/p&gt;
&lt;p&gt;If more than &quot;no context&quot; will live at the CMS layer, decide up-front a clear sense of how much or how little context needs to be configurable. Where you land on this scale from little to most will ripple through budget, timeline and architecture.&lt;/p&gt;
&lt;h3&gt;We need Context Management Systems&lt;/h3&gt;
&lt;p&gt;Second, I think there&apos;s a larger opportunity for technology to step in and formalize context further as a major step in making the web. &lt;em&gt;This is the Context Management System.&lt;/em&gt; Create a separate space, that lives decoupled from content and code and streamlines the ability to marry content with an experiential context it is intended to be presented in. But why? That seems like a lot of added complexity!&lt;/p&gt;
&lt;p&gt;Yes, it is more architecture and may not apply to smaller systems that can work out an agreed-upon shared custody of context. But I do firmly believe that the cleaner delineation of context is extremely valuable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Content managers can focus on &lt;em&gt;content&lt;/em&gt; management and dial way back on the required technical proprietary understanding of system configuration so often thrown onto them.&lt;/li&gt;
&lt;li&gt;In tandem, the effort spent by the development team to customize the context configuration experience, which is driven by the specific technical expertise of an individual or team of content managers, can be greatly reduced.&lt;/li&gt;
&lt;li&gt;A new role, &lt;em&gt;context manager&lt;/em&gt; is born. And maybe this still ends up falling on the content manager, or a developer, or maybe more strategically assigned to a designer. Whether it increases headcount or not, being able to assign this role specifically unattached to also managing content or also maintaining codebase is massively valuable.&lt;/li&gt;
&lt;li&gt;A context management system can focus on shaping content into an explicit experience, but also many explicit experiences using the same content, thus re-establishing the whole point of moving into a headless CMS.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What does a Context Management System look like?&lt;/h3&gt;
&lt;p&gt;Managing context already exists across many platforms. The Wordpress Block Editor is a great example, it marries content and context management in a pretty good way. Some headless CMS products also provide various strategies to hook into your front-end code creating sort of an alternative path to the content creation experience. These are fine, and definitely fall under the bucket of strategic context management, but they aren&apos;t quite what I&apos;m proposing here.&lt;/p&gt;
&lt;p&gt;A context management system is one that sits much more firmly between the CMS and the final end-point. It takes on all of the deep ties to the front-end experience, but provides an interface that does not require intimate knowledge of code.&lt;/p&gt;
&lt;p&gt;It can still provide an environment to experiment with context before content is finalized, but also brings UX design back into the conversation long after a web design has been finalized and approved. Where the maintenance phase of a website often consists of either: just change the content, or develop a new feature, with little in-between. Configuration at the contextual layer allows you to keep the content but change the experience. With having implemented a design system of components, this now becomes a massively useful tool to experiment with and apply that design system in an agile manner.&lt;/p&gt;
&lt;p&gt;More technically, a Context Management System might have these features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A way to visualize multiple contexts at once at a high level.&lt;/li&gt;
&lt;li&gt;A way to apply content into a context (of course).&lt;/li&gt;
&lt;li&gt;A way to map content fields to possible contextual destinations within components.&lt;/li&gt;
&lt;li&gt;A way to transform content to fit certain contexts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Separating context and content is worth considering&lt;/h2&gt;
&lt;p&gt;Context is worth considering, separately from content and the front-end implementation. Especially in the case of working with design systems, your context options are defined up-front and fit this idea quite well. Just as content evolves, the design and experience with which it is conveyed should evolve too, sometimes separately from the message. I think we need tools that specifically focus on this capability that also allow for autonomy around developing new features and content. Context Management Systems, and Context Managers could be that next focused key that help large digital systems work effectively over a longer lifespan.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/we-need-context-management-systems.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Consider Astro over WordPress for your Brand&apos;s Website</title><link>https://andrewwalpole.com/blog/consider-astro-over-wordpress/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/consider-astro-over-wordpress/</guid><description>Are you a company looking to build a new website? As someone who built company websites for many years, I think you should consider not going with another WordPress site.</description><pubDate>Sun, 29 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Consider Astro over WordPress for your Brand&apos;s Website&lt;/h1&gt;
&lt;p&gt;I&apos;ve spent the last handful of years building lots of websites for company brands big and small. As the lead architect and stack decision-maker for those projects, I&apos;ve come to one major conclusion: &lt;code&gt;WordPress is great if you get it right, but if you get it right, it&apos;s quite expensive.&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;How is WordPress so expensive?&lt;/h2&gt;
&lt;p&gt;You might be thinking, &quot;But WordPress is free!&quot;&lt;/p&gt;
&lt;p&gt;Sure, the software and often some of the most critical plugins and themes are free or even quite affordable. If your content is your main focus for your new site, go ahead and grab a nice theme and have fun. I&apos;m specifically talking about you, dear future-someone&apos;s-client, about your future-new custom website that showcases your brand and products/services, setting your business apart from your competition.&lt;/p&gt;
&lt;p&gt;WordPress ends up being expensive in three ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Development&lt;/strong&gt;: Getting it right means building a robust, highly-composable set of componentry to fit your brand. This can be a large up-front investment into the development of your site that will help fight against &lt;a href=&quot;https://andrewwalpole.com/blog/digital-entropy/&quot;&gt;Digital Entropy&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: Someone needs to learn and know how to keep your site humming, both on the technical back-end and on the content-focused front-end.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Platform Dependence&lt;/strong&gt;: WordPress is a great CMS, but some of the shortcuts that make things easy sacrifice separation of concerns. And so while content is manageable it is often tightly coupled to context, and thus as your site&apos;s content grows so does your dependence on WordPress and your specific theme. You will invest more in evolving it and keeping it going rather than having the option to clearly pivot as your business might demand.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Astro is a great WordPress alternative&lt;/h2&gt;
&lt;p&gt;There are lots of alternative technologies to build your website in these days. Some are entire platforms that offer as much as WordPress might, often with the same issues listed above. Some are more rigid, trading in customizability for simplicity. And some are a bit more barebones, where it takes a much more technical hand to pieces together functionalities to cover all the facets of building and managing a website.&lt;/p&gt;
&lt;p&gt;I&apos;m here to recommend &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;. Astro lets you build static or server-rendered websites. It&apos;s specifically built to handle major front-end frameworks developers like to build sites in and provides a developer experience that keeps dev focused on building the site itself and not just the inter-workings of it.&lt;/p&gt;
&lt;p&gt;There are four main reasons I think Astro is worth a long look:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;You can get a custom site built, and while it may not be cheaper, front-end development can be done very efficiently and most of that cost will go into bringing the site to life and not the underlying stack.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Coupling Astro with a headless CMS is really easy, but also not required. This could let you take things in phases, starting without a CMS and then quickly integrating one as content is needed to be managed quickly. This also solves the separation of concerns issue mentioned above. A headless CMS provides a clear opportunity to build your content in a way that is reusable even as your business needs require you to make drastic changes to your website.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Continuing off of the last point, Astro&apos;s ability to adapt to many popular web frameworks also puts you in a prime position to grow into a Design System of componentry. Even building new websites, or adjacent campaign sites with the same underlying Astro stack would be easy to do.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fighting digital entropy is, in most cases, not worth it. Instead, &lt;em&gt;embrace it&lt;/em&gt;. Websites are more effective as they closely align to your business. But business evolves, knocking that synchronicity out of step. To remain highly effective, redoing some or all of your website every few years is a normal thing these days, and Astro makes that easy.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Lastly, what sets Astro apart from all the other WordPress alternatives? For me, it&apos;s their server-rendering and hybrid-rendering offering. Astro is the best of static site generation: fast, performant sites &lt;em&gt;and&lt;/em&gt; the best of server-rendered sites: dynamic, realtime content and functionality. They&apos;ve cracked a long-avoided nut that opens up a huge amount of potential for building great sites.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;WordPress is still a contender, but keep an open mind&lt;/h2&gt;
&lt;p&gt;If you have the budget and the need to manage a site very deeply and often with non-technical folks, I think WordPress is really hard to look away from. The block editor and the experience you can have in building and maintaining sites with the right componentry is great.&lt;/p&gt;
&lt;p&gt;But if those things aren&apos;t all true, I encourage you to take a look at Astro. Even as young of a framework that it is, it has the capabilities you need to build a beautiful, functional and scalable site.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/consider-astro-for-your-brands-website.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>The New Astro Content Collections are Great</title><link>https://andrewwalpole.com/blog/the-new-astro-content-collections-are-great/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/the-new-astro-content-collections-are-great/</guid><description>Astro 2.0 just landed, and with it some great new features including Content Collections. Let&apos;s have a look!</description><pubDate>Fri, 10 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;The New Astro Content Collections are Great&lt;/h1&gt;
&lt;p&gt;It&apos;s official, I think I&apos;m an Astro fanboy.&lt;/p&gt;
&lt;p&gt;If you&apos;re here, you might notice the site has been spruced up a bit, and that&apos;s because I just spent the last few evenings moving my entire site over to the &lt;a href=&quot;https://astro.build/blog/astro-2/&quot;&gt;newly landed Astro 2.0&lt;/a&gt;. It was a joy to build. And while I quite loved my old Eleventy setup too, I&apos;m glad I made the move.&lt;/p&gt;
&lt;p&gt;But I&apos;m not ready to gush about all things Astro yet, right now I just want to talk about a specific new feature: &lt;a href=&quot;https://docs.astro.build/en/guides/content-collections/&quot;&gt;Content Collections&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Content collections are a clever and simple file-based CMS&lt;/h2&gt;
&lt;p&gt;The way Content Collections work in Astro is you have a directory called &lt;code&gt;content/&lt;/code&gt; in your project &lt;code&gt;src/&lt;/code&gt;. In there, you create more directories, one for each content-type (or content collection) you want to manage. Within those content-type buckets you can put markdown or MDX files. The classic use-case is for a blog, like this one!&lt;/p&gt;
&lt;p&gt;With metadata in the frontmatter and the content in the body, your markdown files act as individual content collection entries. The file-based nature of it is extremely robust, and I love that it&apos;s fully separated from the context in which the content will be placed.&lt;/p&gt;
&lt;p&gt;In my case, this site currently has three Content Collections: &lt;code&gt;blog/&lt;/code&gt;, &lt;code&gt;work/&lt;/code&gt;, and &lt;code&gt;site/&lt;/code&gt;. Each representing different content pieces that I may want to query for and place across my site.&lt;/p&gt;
&lt;h2&gt;Using Content Collections is easy&lt;/h2&gt;
&lt;p&gt;There are two main ways to get content from Content Collections in Astro:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can query content by its file slug with &lt;code&gt;getEntryBySlug()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or query an entire collection using &lt;code&gt;getCollection()&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Querying by slug:&lt;/h3&gt;
&lt;p&gt;In my &lt;code&gt;site/&lt;/code&gt; content collection I only have an &lt;code&gt;about.md&lt;/code&gt; file for now. But as I build out sections and pages with content, I can add new files to the collection to pull out and place into areas of my site. It&apos;s a great use-case to query the content by slug because I&apos;ll likely know exactly which       single entry I&apos;ll want to put in a specific place.&lt;/p&gt;
&lt;p&gt;In my &lt;code&gt;components/&lt;/code&gt; directory I have a helper component called &lt;code&gt;SiteContent.astro&lt;/code&gt; to make it easy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import { getEntryBySlug } from &apos;astro:content&apos;;

const { slug } = Astro.props;
const entry = await getEntryBySlug(&apos;site&apos;, slug);
const { Content } = await entry.render();
---

&amp;lt;Content /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This component imports &lt;code&gt;getEntryBySlug()&lt;/code&gt; and calls it using a passed-in &lt;code&gt;slug&lt;/code&gt; prop to query for a specific entry in my site content collection. Calling &lt;code&gt;render()&lt;/code&gt; on the retrieved entry returns a &lt;code&gt;Content&lt;/code&gt; component that can be used in the component body to render the markdown of the entry. If you needed to grab any of the frontmatter metadata for the entry, you can find all of that in the &lt;code&gt;entry.data&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Now in my &lt;code&gt;index.astro&lt;/code&gt; I can just add a simple &lt;code&gt;&amp;lt;SiteContent slug=&quot;about&quot; /&amp;gt;&lt;/code&gt; to render my &lt;code&gt;about.md&lt;/code&gt; content.&lt;/p&gt;
&lt;h3&gt;Querying multiple entries:&lt;/h3&gt;
&lt;p&gt;Getting multiple entries is not much harder. Creating the listing for my blog page looks a little like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import CollectionListItem from &apos;./CollectionListItem.astro&apos;;
import { getCollection } from &apos;astro:content&apos;;

const {collection = &apos;blog&apos;} = Astro.props

const allPosts = await getCollection(collection);

const { limit = Infinity, filter = [&apos;published&apos;], interactive = false} = Astro.props;
const filteredLimitedOrderedPosts = allPosts.reverse().filter(post =&amp;gt; post.data.tags?.some( tag =&amp;gt; filter.includes(tag) ) ).slice(0, limit);
---

{ allPosts.map( post =&amp;gt; &amp;lt;CollectionListItem post={post} /&amp;gt; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, given a passed-in &lt;code&gt;collection&lt;/code&gt; prop, we can call &lt;code&gt;getCollection(collection)&lt;/code&gt; to retrieve all of the entries as an array. From there it&apos;s a matter of massaging the array with features like sorting, filtering and limiting before sending them off to be rendered via another &lt;code&gt;&amp;lt;CollectionItem /&amp;gt;&lt;/code&gt; component.&lt;/p&gt;
&lt;h3&gt;And there&apos;s more!&lt;/h3&gt;
&lt;p&gt;I didn&apos;t touch on it here, but Content Collections have another great feature: a full schema definition and checking system that keeps your content conforming to the required metadata you need to keep your site humming along properly.&lt;/p&gt;
&lt;p&gt;All-in-all, I&apos;m really enjoying Astro as a site builder. It&apos;s fast and intuitive, and if the slickness of Content Collections is any sign, I can&apos;t wait for this framework to continue to grow and mature.&lt;/p&gt;
&lt;p&gt;If you&apos;d like to see more of how I rebuilt this site in Astro, you can have a peek at the source code over on GitHub: https://github.com/walpolea/andrewwalpole.com.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/the-new-astro-content-collections-are-great.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Sprinkling Interaction with Petite-Vue</title><link>https://andrewwalpole.com/blog/sprinkling-interaction-with-petite-vue/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/sprinkling-interaction-with-petite-vue/</guid><description>How do you sprinkle in interactive elements? For many of us it used to be jQuery, and now maybe it&apos;s vanilla JS or you&apos;ve graduated up to a larger framework like React, Vue or Svelte. Here&apos;s my case for why you ought to take a look at petite-vue.</description><pubDate>Tue, 17 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Sprinkling Interaction with Petite-Vue&lt;/h1&gt;
&lt;p&gt;How do you sprinkle on a little bit of interactivity to a website? For many of us, jQuery was the definitive answer; from simple to complex, jQuery fit the bill for a long time. With many of those core DOM APIs getting back-filled into vanilla JavaScript, we have long-since moved away from jQuery.&lt;/p&gt;
&lt;p&gt;But even years later, I can&apos;t help but still see a gap left in its wake.&lt;/p&gt;
&lt;h2&gt;The Gap&lt;/h2&gt;
&lt;p&gt;Vanilla JS is better than ever, don&apos;t get me wrong, but the work to get things hooked-up tends to inflate pretty quickly as your features grow. What you thought would be a sprinkling turns into a dousing. It&apos;s too easy to step too far and find yourself nearly writing your own framework.&lt;/p&gt;
&lt;p&gt;On the other side, many of us have gone full hog-wild into a framework. React, Svelte, or Vue are great places to have all the interactivity you want at your finger-tips. But with them, comes an almost entirely new workflow to creating a website than just standing up a few &lt;code&gt;.html&lt;/code&gt;, &lt;code&gt;.css&lt;/code&gt;, and &lt;code&gt;.js&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;So that&apos;s the gap: &lt;em&gt;Vanilla JS is not structured enough, and the big frameworks are too structured&lt;/em&gt;. Isn&apos;t there something in the middle?&lt;/p&gt;
&lt;h2&gt;Something in the middle: Petite-vue&lt;/h2&gt;
&lt;p&gt;I&apos;ve been itching to write this post for a few reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;I love &lt;a href=&quot;https://github.com/vuejs/petite-vue&quot;&gt;petite-vue&lt;/a&gt;&lt;/em&gt;, so I&apos;ll share the love, and&lt;/li&gt;
&lt;li&gt;when it landed people were super excited by the novelty of it – &lt;em&gt;a mini-Vue, how cute!&lt;/em&gt; – but I don&apos;t think it got the full appreciation it deserved.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Petite-vue fills the gap perfectly for me. It&apos;s easily added to a site or just a single page, can be written portably and reused, is quite small (&lt;code&gt;6kb&lt;/code&gt;), and provides you with a declarative approach to interactivity that makes it quick and easy to super-charge your front-end without entirely taking it over.&lt;/p&gt;
&lt;p&gt;And the bonus, for Vue-inclined folks, it is a sub-set of Vue syntax, so the learning curve is almost nonexistent.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;There are a few ways you can &lt;a href=&quot;https://github.com/vuejs/petite-vue#usage&quot;&gt;get started with petite-vue&lt;/a&gt;. And certainly, the docs will get you up to speed with the technical basics faster than this post will, but I want to peek into some of the ways I find the library most useful.&lt;/p&gt;
&lt;h3&gt;Managing global page state&lt;/h3&gt;
&lt;p&gt;Petite-vue is great when you want your entire page to react and communicate with itself. Here&apos;s a few lines of JavaScript to get us going:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
  import { createApp } from &apos;https://unpkg.com/petite-vue?module&apos;
  createApp({
    //SOME GLOBAL STATE PROPERTIES &amp;amp; FUNCTIONS
    isDarkMode:false
  }).mount();
&amp;lt;/script&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will mount petite-vue to the entire document, allowing you to immediately start using the declarative syntax of it within your html. If you just want to toggle some state and update some classes or css variables based on user interaction, this might be the route for you!&lt;/p&gt;
&lt;p&gt;In your HTML you can now access, modify and read your state object to react to the page accordingly:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; v-model=&quot;isDarkMode&quot;&amp;gt; Dark Mode
&amp;lt;/label&amp;gt;
&amp;lt;div :class=&quot;{darkmode: isDarkMode}&quot;&amp;gt;
  Hello World
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;isDarkMode&lt;/code&gt; gets assigned to the checked state of the checkbox via the &lt;code&gt;v-model&lt;/code&gt; directive. And on the div, the class, &lt;code&gt;darkmode&lt;/code&gt; is present only when &lt;code&gt;isDarkMode&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;450&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;abqpjGy&quot; data-user=&quot;walpolea&quot; style=&quot;height: 450px; 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/walpolea/pen/abqpjGy&quot;&amp;gt;
Getting Started with Petite-vue&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;&amp;lt;hr&amp;gt;&lt;/p&gt;
&lt;h3&gt;Functional components&lt;/h3&gt;
&lt;p&gt;My next favorite use of petite-vue is to build little functional components. Let&apos;s build a simple content accordion. The big difference here is we are no longer attaching petite-vue to the entire page. Instead we&apos;re going to surgically inject it into the markup that we want to imbue with super powers.&lt;/p&gt;
&lt;p&gt;I have to point out that this is very much a middle-ground between full-page interaction and traditional component frameworks. The goal isn&apos;t to fully encapsulate the component into a single function call (though &lt;em&gt;petite-vue can do that&lt;/em&gt;). Instead, it serves as a way to inject functionality into your markup, allowing the markup and css to stay bespoke while the functionality is consistent.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;450&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;ExQWyMv&quot; data-user=&quot;walpolea&quot; style=&quot;height: 450px; 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/walpolea/pen/ExQWyMv&quot;&amp;gt;
Getting Started with Petite-vue 2&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;In the JavaScript, we create a function called, &lt;code&gt;accordion&lt;/code&gt;. That function returns the scope object, which can contain properties and functions that the petite-vue component uses. All we need is an &lt;code&gt;isOpen&lt;/code&gt; state and a function to calculate the height of the toggleable content when it&apos;s opened or closed.&lt;/p&gt;
&lt;p&gt;In the html, the &lt;code&gt;v-scope&lt;/code&gt; attribute is what sets the whole thing in motion. Petite-vue binds that object to this element, and all children now have access. You can see we&apos;re using &lt;code&gt;isOpen&lt;/code&gt; a bunch to set attributes and make the whole accordion work. Even modifying CSS variables, though a bit ugly to look at:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:style=&quot;{ &apos;--height&apos;: calculateHeight($el) }&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;is super powerful here, allowing our CSS to play a hand in how this works. Because &lt;code&gt;isOpen&lt;/code&gt; is referenced inside of &lt;code&gt;calculateHeight()&lt;/code&gt; petite-vue knows to call it when that property changes 🤯!&lt;/p&gt;
&lt;p&gt;The last point is that we can reuse this &lt;code&gt;accordion&lt;/code&gt; scope across multiple elements, they act as completely independent instances of petite-vue.&lt;/p&gt;
&lt;p&gt;&amp;lt;hr&amp;gt;&lt;/p&gt;
&lt;h3&gt;Micro-web-apps&lt;/h3&gt;
&lt;p&gt;I absolutely will cover more use-cases in the future, but I want to leave you with this one.&lt;/p&gt;
&lt;p&gt;Not only does petite-vue fill the gap of website interaction for me, it&apos;s comfortable playing in web-app territory without completely taking over your build process. You can even scope your micro-web-app right into your site.&lt;/p&gt;
&lt;p&gt;Here&apos;s a small deck of cards micro-web-app in petite-vue:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;450&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;QWgORQN&quot; data-user=&quot;walpolea&quot; style=&quot;height: 450px; 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/walpolea/pen/QWgORQN&quot;&amp;gt;
Petite-vue Deck of Cards&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;The app is comprised of two scopes, one for the outer shell of the app (&lt;code&gt;DeckOfCards&lt;/code&gt;), setting up the deck and interface. The second is for each &lt;code&gt;Card&lt;/code&gt; allowing me to easily componentize how it is built and rendered. It&apos;s incredibly powerful that you can compose multiple scopes together this way, allowing you to manage multiple parts of the app separately.&lt;/p&gt;
&lt;p&gt;Certainly there would come a point at which it might make more sense to switch this into a larger framework, but the biggest point I want to illustrate is that through all of these demos, petite-vue allows you to stay as simple or go as complex as you need to, very much to akin to what we had with jQuery; it fills the gap. It&apos;s an amazing tool for sprinkling functionality into sites, or prototyping a larger app idea. I hope you&apos;ll give it a try!&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Progressive Enhancement with Petite-vue</title><link>https://andrewwalpole.com/blog/progressive-enhancement-with-petite-vue/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/progressive-enhancement-with-petite-vue/</guid><description>Rather than having to completely rely on the client to render your content when you want to make it interactive or functional, progressively enhance it with petite-vue.</description><pubDate>Tue, 24 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Progressive Enhancement with Petite-vue&lt;/h1&gt;
&lt;p&gt;I recently wrote about how petite-vue is a great way to &lt;a href=&quot;/blog/sprinkling-interactivity-with-petite-vue&quot;&gt;sprinkle in interactivity&lt;/a&gt;. There&apos;s a downside to putting all your eggs in the JavaScript basket though, especially when it involves rendering content. There&apos;s a whole fuzzy world of, &lt;em&gt;just because you can, doesn&apos;t mean you should&lt;/em&gt; web dev things, and this is definitely one of them.&lt;/p&gt;
&lt;p&gt;Similar to Single-Page Applications (SPAs), you&apos;ll have a rougher time with SEO, because the content isn&apos;t there when the page loads, it&apos;s being added at runtime. Also, if JavaScript is turned off, your users will get served up a whole lot of nothin&apos;. &lt;a href=&quot;https://css-tricks.com/should-a-website-work-without-javascript/&quot;&gt;You can debate&lt;/a&gt; that we&apos;re beyond a world that needs the web to work without JavaScript, but I like the idea of approaching JavaScript as a tool for progressive enhancement; the concept has too many benefits to out-right ignore.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;To illustrate the problem, here&apos;s a pen showing a searchable list of grocery items being rendered to the page using petite-vue. Why not just write HTML? Because we want to enhance this list by making it searchable, and petite-vue makes that elegantly easy.&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;v-for&lt;/code&gt; directive to create &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;&apos;s from a &lt;code&gt;filteredList&lt;/code&gt; getter that filters the original list with the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; value. As the search term in the input updates, the &lt;code&gt;filteredList&lt;/code&gt; getter reruns, causing the &lt;code&gt;v-for&lt;/code&gt; to rerender the list all in real-time.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;450&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;QWQqNKj&quot; data-user=&quot;walpolea&quot; style=&quot;height: 450px; 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/walpolea/pen/QWQqNKj&quot;&amp;gt;
Progressively Enhanced Petite-Vue&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;&amp;lt;aside&amp;gt;
As an aside, it&apos;s a shame that we have to take full control of rendering the content away from HTML to make this work easily. This would be one of the possible scenarios that could be fixed by &amp;lt;a href=&quot;/blog/the-fourth-language&quot;&amp;gt;a fourth language&amp;lt;/a&amp;gt; centered around keeping track of interactive page state.
&amp;lt;/aside&amp;gt;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;The Solution&lt;/h2&gt;
&lt;p&gt;Let&apos;s fix the problem with a progressive enhancement pattern in petite-vue.&lt;/p&gt;
&lt;p&gt;Effectively, we&apos;re going to provide the list (without search functionality) as static markup, and then use petite-vue to rerender that content with its own markup. It&apos;s essentially a bait-and-switch of static content with interactive content. As long as your markup matches, the user won&apos;t notice any large content shifting, except for where there are added functional pieces, like the search box.&lt;/p&gt;
&lt;p&gt;Hit &lt;code&gt;Rerun&lt;/code&gt; down in the bottom right to see the progressive enhancement happen. For dramatic effect, there&apos;s a 2 second &lt;code&gt;setTimeout&lt;/code&gt; delay to give you time to see the original static content, and then watch the content become enhanced. In reality, with a quick loading site, users likely won&apos;t even notice the transition happen.&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;450&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;qBxPqmY&quot; data-user=&quot;walpolea&quot; style=&quot;height: 450px; 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/walpolea/pen/qBxPqmY&quot;&amp;gt;
Progressively Enhanced Petite-Vue&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;
&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;The Progressive Enhancement Pattern&lt;/h2&gt;
&lt;p&gt;Let&apos;s pull apart the example and break down the pattern, because that&apos;s the bit that can be applied to a whole host of situations where you want to use petite-vue to progressively enhance your content.&lt;/p&gt;
&lt;h3&gt;Javascript&lt;/h3&gt;
&lt;p&gt;First let&apos;s get the JavaScript out of the way, because it&apos;s important that I don&apos;t leave you in the dark, but it really has little bearing on the pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { createApp } from &apos;https://unpkg.com/petite-vue?module&apos;;

const searchableList = ({list}) =&amp;gt; {
  return {
    list,
    initialized:false,
    search:&apos;&apos;,
    //when the scope mounts, set initialized to true
    async mounted() {
      await new Promise(resolve =&amp;gt; setTimeout(resolve, 2000)); //artificial delay
      this.initialized = true;
    },
    //return a filtered version of the full list based on the search term
    get filteredList() {
      if( this.search ) {
        return this.list.filter( g =&amp;gt; g.toLowerCase().includes(this.search.toLowerCase()) )
      }
      //otherwise, no search term, return the full list
      return this.list;
    }
  };
}

//this tells petite-vue to look for v-scope directives on the page,
//and we register the ability to use the searchableList scope we built
createApp({
  searchableList
}).mount()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h3&gt;HTML&lt;/h3&gt;
&lt;p&gt;The HTML is really where we can call out the progressive enhancement pattern. We separate the block into two main parts:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;container&quot; v-scope=&quot;searchableList({list:groceries})&quot; @vue:mounted=&quot;mounted&quot;&amp;gt;
  &amp;lt;template v-if=&quot;initialized&quot;&amp;gt;
    &amp;lt;!--   This only shows when petite-vue runs and sets initialized to true  --&amp;gt;
    &amp;lt;h1&amp;gt;Enhanced with Petite-vue&amp;lt;/h1&amp;gt;
    &amp;lt;input type=&quot;text&quot; v-model=&quot;search&quot; placeholder=&quot;search groceries&quot;&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li v-for=&quot;item in filteredList&quot;&amp;gt;{{item}}&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/template&amp;gt;
  ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first block, within a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag will be the petite-vue enhancement. we will add the &lt;code&gt;v-if=&quot;initialized&quot;&lt;/code&gt; directive so that this tag will not render unless initialize is evaluated by petite-vue as true. The beauty here is that HTML won&apos;t render a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag into the page either, so if JavaScript hasn&apos;t run, we never see this content. Inside is all the stuff from the earlier demo, using &lt;code&gt;v-for&lt;/code&gt; and such.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  ...
  &amp;lt;div v-else&amp;gt;
    &amp;lt;!--   This content would be statically or server generated and hide when petite-vue runs  --&amp;gt;
    &amp;lt;h1&amp;gt;Statically Rendered Content&amp;lt;/h1&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li&amp;gt;Milk&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Eggs&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Bread&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Jam&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Cheese&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Butter&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;Mustard&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
    &amp;lt;script&amp;gt;
      //this could also be server-generated, which is why I put it up here in the html block
      const groceries = [&apos;Milk&apos;, &apos;Eggs&apos;, &apos;Bread&apos;, &apos;Jam&apos;, &apos;Cheese&apos;, &apos;Butter&apos;, &apos;Mustard&apos;];
    &amp;lt;/script&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second part is your statically generated content contained in &lt;code&gt;&amp;lt;div v-else&amp;gt;&lt;/code&gt;. Once again being tricky, without JavaScript running, the &lt;code&gt;v-else&lt;/code&gt; gets ignored and the page will render this content as normal. Here you might actually be using something like php or nunjucks or liquid to dynamically generate this markup. Whatever it is, &lt;em&gt;it&apos;s just HTML&lt;/em&gt;, either static or server-generated.&lt;/p&gt;
&lt;p&gt;The last added trick is that we need to pass the list of things into petite-vue as JavaScript, so you&apos;ll also see a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag where we can generate the array petite-vue needs to recreate the list itself. Again, something that can be generated with a templating language.&lt;/p&gt;
&lt;p&gt;That&apos;s it! When JavaScript is disabled, the static content is displayed by default. But when petite-vue mounts, &lt;code&gt;initialized&lt;/code&gt; becomes &lt;code&gt;true&lt;/code&gt; and the static content hides and the enhanced petite-vue content shows.&lt;/p&gt;
&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;It&apos;s a little more work, but in a lot of production cases, I find this pattern to be very approachable as a way to provide static content that is then progressively enhanced by JavaScript. I&apos;ve shipped this pattern on a few sites already with great success (including &lt;a href=&quot;/blog&quot;&gt;this blog&apos;s&lt;/a&gt; tag filtering functionality). The pattern can be broken down as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Put your enhanced petite-vue markup in a &lt;code&gt;&amp;lt;template v-if=&quot;initialized&quot;&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Put your static content in a sibling &lt;code&gt;&amp;lt;div v-else&amp;gt;&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;Also render out a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag that defines the data petite-vue needs to render its own version of the content.&lt;/li&gt;
&lt;li&gt;Set initialized to &lt;code&gt;true&lt;/code&gt; when petite-vue mounts, this will hide the static content and show the enhanced content.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Blog Post Image Generator with HTML2Canvas</title><link>https://andrewwalpole.com/blog/blog-post-image-generator-with-html2canvas/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/blog-post-image-generator-with-html2canvas/</guid><description>Here&apos;s a practical look at using html2canvas to generate images for blog posts. You might be looking at said image right now!</description><pubDate>Sun, 29 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Blog Post Image Generator with HTML2Canvas&lt;/h1&gt;
&lt;p&gt;I&apos;m a big fan of the &lt;a href=&quot;https://html2canvas.hertzen.com/&quot;&gt;HTML2Canvas&lt;/a&gt; library. It has some quirks and is less maintained (lots of &lt;a href=&quot;https://github.com/niklasvh/html2canvas/issues&quot;&gt;issues&lt;/a&gt; and open &lt;a href=&quot;https://github.com/niklasvh/html2canvas/pulls&quot;&gt;PRs&lt;/a&gt;) than I would like to see – actually I worry quite a bit about it becoming too stale to use, just because it can be so useful!&lt;/p&gt;
&lt;p&gt;Here&apos;s a look at one way I&apos;m using it to generate preview images for my blog posts, it&apos;s so simple, my production version of it just lives in codepen.&lt;/p&gt;
&lt;p&gt;Before I get to that, I just want to clarify that this is not &lt;a href=&quot;https://css-tricks.com/automatic-social-share-images/&quot;&gt;automatic social share images&lt;/a&gt;. Though this could be a piece of getting there, the biggest issue is that a hands-off solution will require you to automate the &lt;em&gt;&quot;run in browser&quot;&lt;/em&gt; portion of an automated process, which you can pull off with something like &lt;a href=&quot;https://github.com/puppeteer/puppeteer&quot;&gt;puppeteer&lt;/a&gt;, but that&apos;s not in scope of this post.&lt;/p&gt;
&lt;h2&gt;The Setup&lt;/h2&gt;
&lt;p&gt;The setup is mostly focused on getting some HTML and CSS up that I would like to render as an image.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;featured-image&quot; :class=&quot;{ square: isSquare }&quot;&amp;gt;
    &amp;lt;h1&amp;gt;{{title}}&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;{{summary}}&amp;lt;/p&amp;gt;
    &amp;lt;div class=&quot;site-tag&quot;&amp;gt;
      &amp;lt;svg viewBox=&quot;0 0 1158 1201&quot; fill=&quot;none&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&amp;gt;
        &amp;lt;!-- truncated for space --&amp;gt;
      &amp;lt;/svg&amp;gt;
      &amp;lt;div class=&quot;tag-content&quot;&amp;gt;
        &amp;lt;p&amp;gt;Andrew Walpole &amp;amp;copy; {{year}}&amp;lt;/p&amp;gt;
        &amp;lt;p&amp;gt;andrewwalpole.com&amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&apos;ll notice some &lt;code&gt;{{petite-vue}}&lt;/code&gt; content placeholders here, but it&apos;s not required. You could hardcode the content in, or use vanilla JavaScript to update it. But for me, petite-vue is a quick way to hook the content up to text inputs that I can easily use to generate a new custom image.&lt;/p&gt;
&lt;p&gt;Here are those form fields getting hooked up to the content:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label&amp;gt;
  Post Title:
  &amp;lt;input type=&quot;text&quot; v-model=&quot;title&quot;&amp;gt;
&amp;lt;/label&amp;gt;
&amp;lt;label&amp;gt;
  Post Summary:
  &amp;lt;textarea v-model=&quot;summary&quot; cols=50 rows=5&amp;gt;&amp;lt;/textarea&amp;gt;
&amp;lt;/label&amp;gt;
&amp;lt;label&amp;gt;
  &amp;lt;input type=&quot;checkbox&quot; v-model=&quot;isSquare&quot;&amp;gt; Square
&amp;lt;/label&amp;gt;
&amp;lt;button @click=&quot;saveDown&quot;&amp;gt;Save Image&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As mentioned, this provides a simple interface to allow the content to be customized quickly. The title and description can be entered, and I even have an option to toggle between wide and square layouts.&lt;/p&gt;
&lt;h2&gt;The Secret Sauce&lt;/h2&gt;
&lt;p&gt;The last but most special bit is to use &lt;code&gt;html2canvas&lt;/code&gt; to generate an image from the html, and it&apos;s pretty easy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import &quot;https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js&quot;;

saveDown() {
  const containerToRender = document.querySelector(&apos;.featured-image&apos;);
  html2canvas(
    containerToRender,
    { 
      allowTaint: true, 
      useCORS: true, 
      backgroundColor: &quot;transparent&quot;,  
      width: containerToRender.offsetWidth, 
      height: containerToRender.offsetHeight,
    }
  ).then(canvas =&amp;gt; {
    const previewArea = document.querySelector(&apos;.preview-area&apos;);
    previewArea.innerHTML = &quot;&quot;;
    previewArea.appendChild(canvas);
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We &lt;code&gt;querySelector&lt;/code&gt; for the container we want to imagify, then call &lt;code&gt;html2canvas( &amp;lt;container&amp;gt;, &amp;lt;options&amp;gt; )&lt;/code&gt;. It returns a promise that when fulfilled will give you back a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element. We can then just append that canvas into our HTML and you can right click it to &lt;code&gt;Save Image As...&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A few things to note. We&apos;re using the container&apos;s width and height to render the image, this allows our size toggle to work, and will also render a different image if the browser window is constraining the size. You can fix these values if you want a consistent size. Also, I had some issues with the SVG positioning and the opacity settings on it, also the &lt;code&gt;::before&lt;/code&gt; on the title renders oddly the first time you save it, and then correctly the second time. These are those quirks I mentioned at the beginning.&lt;/p&gt;
&lt;p&gt;The image that is generated is a PNG, which is not well optimized. I find myself needing to run it through &lt;a href=&quot;https://squoosh.app&quot;&gt;squoosh&lt;/a&gt; before using it, and that&apos;s a little annoying, but in my search for a client-side optimizer that could eliminate this step I have come up short.&lt;/p&gt;
&lt;p&gt;Finally here&apos;s the whole thing for you to see and play with:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;800&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;VwQbzdq&quot; data-user=&quot;walpolea&quot; style=&quot;height: 800px; 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/walpolea/pen/VwQbzdq&quot;&amp;gt;
Blog Post Featured Image Generator&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Lead Great Engineering Manager One-on-ones</title><link>https://andrewwalpole.com/blog/lead-great-engineering-manager-one-on-ones/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/lead-great-engineering-manager-one-on-ones/</guid><description>After years of honing my leadership style, I&apos;ve landed on a one-on-one format that I quite like. Here&apos;s a deep dive into it.</description><pubDate>Mon, 13 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Lead Great Engineering Manager One-on-ones&lt;/h1&gt;
&lt;p&gt;There are lots of different ways to be a great leader. So I want to be clear up-front that this is really what I have found works for me. If you&apos;re a budding manager that hasn&apos;t quite honed their own style, or just looking to try new things, maybe give some of the following a shot.&lt;/p&gt;
&lt;p&gt;Also, there&apos;s nothing really specific to engineering in this; I think it&apos;s a format that any leader can implement.&lt;/p&gt;
&lt;h2&gt;Make it Official&lt;/h2&gt;
&lt;p&gt;The first thing I establish with my one-on-one meetings is that, as much as our team may grow close and comfortable working together – essential for highly productive, happy teams – our one-on-one format is the one place to be formal. That might sound cold or strange at first, but here&apos;s my reasoning:&lt;/p&gt;
&lt;p&gt;The business exists. Its goal is to establish roles, that when fulfilled, make money. But in most cases, the specific folks fit into those roles are not critical to the primary goals of the company. Welcome to capitalism.&lt;/p&gt;
&lt;p&gt;So being formal is my way of bringing that idea to the forefront of the conversation. I&apos;m in a role of leading, which means I&apos;m accountable for the production expected by the business of those roles under me. Let&apos;s take time to talk about how we&apos;re both caught up in that contract.&lt;/p&gt;
&lt;h3&gt;Tactics for making it official&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Being very up-front about it is what works for me. &lt;em&gt;&quot;Hey Joe, welcome to another one-on-one, in this meeting we get a bit more formal and talk through our roles and relationship with the business.&quot;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Follow a tight agenda. I&apos;ll talk more about the agenda items I use in a bit, but having detailed notes about each section of the meeting helps establish that this is a bit of a rehearsed dance.&lt;/li&gt;
&lt;li&gt;Get a little meta about it. &lt;em&gt;&quot;This is your chance to talk to me, but you&apos;re also talking to my role and the company. Any critical feedback you have toward those things is really important and won&apos;t be taken personally.&quot;&lt;/em&gt; Acknowledging that we&apos;re here to talk about how we fit into our roles, and not just about ourselves individually, helps to open the conversation to critical feedback and insights.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Follow a Specific Agenda&lt;/h2&gt;
&lt;p&gt;As I mentioned, I follow a very strict agenda. In a lot of cases most of it is scripted out. Again, I feel like this could be seen negatively, but having that script frees up your brain to think about the specific conversations those topics lead to and not about the overhead of running the meeting. The script is meant to prompt you both to find specific relevant topics to discuss.&lt;/p&gt;
&lt;h3&gt;Set and Reset Expectations&lt;/h3&gt;
&lt;p&gt;First up, we talk about role expectations. I try to be a really hands-off, non-micromanaging manager. But in order to make that work, building up trust and communication with my team is essential. I can help to lay the framework for doing that through clear expectations.&lt;/p&gt;
&lt;p&gt;In the meeting I&apos;ll run through my pre-defined expectations, I might say something like:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Here are my expectations as your leader:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You are open and honest about how the work you&apos;re doing makes you feel, both in capacity and meaning.&lt;/li&gt;
&lt;li&gt;You have a clear understanding of your work priorities, and if you don&apos;t, you actively come to me for clarity.&lt;/li&gt;
&lt;li&gt;When projects are off-track or have the potential to be off-track, you let me know your concerns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Going over each item can feel like it&apos;s overkill over time, but I make a point to stick to it as each instance provides the opportunity to discuss any specific instances where these expectations were or weren&apos;t met.&lt;/p&gt;
&lt;p&gt;We then flip the conversation. &lt;em&gt;&quot;What are your expectations of me?&quot;&lt;/em&gt; Once established, they get worked into the script and I recite what I have interpreted them to be. Here I can ask for critical feedback: &lt;em&gt;&quot;Am I living up to these expectations for you? Do you have any you want to edit or add?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Check-In on Role&lt;/h3&gt;
&lt;p&gt;There&apos;s a little overlap here from the previous section, as my expectations often align to roles. But this section is meant to focus more on how equitable the employer/employee relationship is at any given time. Even though I&apos;m the manager, I try to remove myself mentally from being on one side or the other. Instead, being a liaison or mediator for that relationship.&lt;/p&gt;
&lt;p&gt;Here are some conversation-starter questions for this section:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you feel like you clearly understand your role in the business?&lt;/li&gt;
&lt;li&gt;Are you being asked to do work that doesn&apos;t align with your role?&lt;/li&gt;
&lt;li&gt;What do you think about the company&apos;s culture/vision/direction?&lt;/li&gt;
&lt;li&gt;Are you feeling burnt out at all?&lt;/li&gt;
&lt;li&gt;What&apos;s something you&apos;re doing a lot of today, that you weren&apos;t doing a year ago?&lt;/li&gt;
&lt;li&gt;Do you see any opportunities to change your role?&lt;/li&gt;
&lt;li&gt;What kind of impact do you feel you&apos;re making?&lt;/li&gt;
&lt;li&gt;Are you doing meaningful or important work?&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Reserve a Time for a Specific Topic or Question&lt;/h3&gt;
&lt;p&gt;At this point, I like to cut away from the script a bit and try to come up with a specific question to hone in on. It might be something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How do you think our QA process could be better?&lt;/li&gt;
&lt;li&gt;What technologies are you interested in that you wish we could be using at work?&lt;/li&gt;
&lt;li&gt;What&apos;s something I do that annoys you?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing too broad, just something to quickly solicit feedback on. On my side, individually asking the same question to multiple direct reports helps to track and call out any trends.&lt;/p&gt;
&lt;h3&gt;Set and Track Goal Progress&lt;/h3&gt;
&lt;p&gt;Back on track with the main agenda, we now want to take a look at any individual goals or learning plan items in place for that person. Reiterate what their goals are and ask about progress. If progress is stifled, talk about why. See if goals need to be adjusted and define smaller milestones and tasks for them to accomplish.&lt;/p&gt;
&lt;h3&gt;What&apos;s on Your Mind?&lt;/h3&gt;
&lt;p&gt;By this point, while there probably has been some good conversation, it&apos;s time to give the reins of the meeting over to them. &lt;em&gt;&quot;Is there anything that we didn&apos;t already cover that you want to talk through?&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Providing your folks a chance to completely lead the conversation without a specific topic is really important. I also find it helpful to reiterate that I&apos;m always open and welcoming of critical feedback.&lt;/p&gt;
&lt;h3&gt;Acknowledge Action Items&lt;/h3&gt;
&lt;p&gt;Lastly, recount any action items that came up in the meeting. Align on what they are and when they should be done by.&lt;/p&gt;
&lt;h2&gt;Be Vulnerable and Show Empathy&lt;/h2&gt;
&lt;p&gt;With that, the one-on-one is done. But a few final notes to make. If this is a new team, don&apos;t expect a super rich conversation; you&apos;re still establishing a connection with your team, so it&apos;s okay if it feels a bit robotic. To combat that, find opportunities to show your own vulnerability and empathy. I have found you really get what you give.&lt;/p&gt;
&lt;p&gt;Double-down on the idea of getting meta with the conversation, encourage stepping into 3rd-person view of their role, your role and the business, it really helps keep things objective and not personal.&lt;/p&gt;
&lt;p&gt;Finally, find the right cadence. For me, every one to two months seems good, but make sure you are still less-formally checking-in in-between; this isn&apos;t the only meeting you should be having with your folks regularly, just the most formal of them.&lt;/p&gt;
&lt;p&gt;If you have tips on running great one-on-ones with your team, or have things you like that your leader does, I would love to hear them!&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/lead-great-engineering-manager-one-on-ones.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Productive Developer Experiences</title><link>https://andrewwalpole.com/blog/productive-developer-experiences/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/productive-developer-experiences/</guid><description>Developer experience has ramped up in focus over the last few years as a way to increase platform adoption and developer productivity. Here are the features I think make for a good DX.</description><pubDate>Fri, 24 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Productive Developer Experiences&lt;/h1&gt;
&lt;p&gt;Early in my career, I remember watching the software and web industry shift from being functionally-focused to user-focused. It was a main driving reason why I decided to switch from a software engineering degree to one that incorporated web development and UX design. Today, functionality is often the baseline, and competitive advantage is pursued through the user experience lens.&lt;/p&gt;
&lt;p&gt;There has been another shift in the last few years, though not as overtaking; more complementary. Developer Experience, or DX, is growing as yet another focus point for software to consider as a primary driver of adoption and platform longevity.&lt;/p&gt;
&lt;p&gt;Here are my top features that I think drive productive developer experiences:&lt;/p&gt;
&lt;h3&gt;A well-groomed happy path&lt;/h3&gt;
&lt;p&gt;The experience to get up and running with anything is a make-or-break moment for new users. Too difficult and we&apos;ll &quot;nope!&quot; right out of there. Lesser-considered though, too magical or left unexplained, and the smarter users might also begin to build a case for being skeptical. Your happy path should be well-groomed, like an upkept hiking trail. It should have bridges in the rough spots and not wind to and fro too much.&lt;/p&gt;
&lt;p&gt;What makes a good happy path?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No walls or fences. Providing &lt;a href=&quot;/blog/escape-hatches&quot;&gt;escape hatches&lt;/a&gt; and even calling out where someone might want to deviate from the path later is key.&lt;/li&gt;
&lt;li&gt;If your tech is really flexible, divide it up by use-cases. It&apos;s more paths to maintain, but worth it in the end that you aren&apos;t actually curating a hedge maze.&lt;/li&gt;
&lt;li&gt;Call out the scenery. Don&apos;t just tell us how to do something; why does it work this way? What are some gotchas or limitations worth knowing about?&lt;/li&gt;
&lt;li&gt;Make all of the bells and whistles opt-in. Show me the base experience and the points at which common functionality can be plugged in, but don&apos;t overwhelm me with a &quot;luxury experience&quot; because it quickly leads to cognitive overload.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Docs are the map to your product landscape&lt;/h3&gt;
&lt;p&gt;Your docs need to be great. They need to be updated thoroughly as your product changes and should be split at minimum into three categories: use-cases, features and reference. Your use-cases show me how someone might use your entire product to accomplish a common use-case. Your features need to deep-dive into each special ability your tech has, and all the ways it can be configured and used. And your reference needs to give me an even closer, raw look at the exposed consumer API. What are the expected inputs and outputs of any given thing I&apos;m working with?&lt;/p&gt;
&lt;h3&gt;Features as baby steps&lt;/h3&gt;
&lt;p&gt;I mentioned making optional features as opt-in as possible. Going hand-in-hand with that, features should feel like small steps. Being overwhelmed by the effort of enabling or configuring a feature is one of the biggest reasons developer tech can be abandoned. Small, easy steps lessen that load. I shouldn&apos;t have to do 15 steps to accomplish something, and even worse, if I get intimidated or change my mind on step 7, things shouldn&apos;t be in a broken state.&lt;/p&gt;
&lt;h3&gt;Useful error messages&lt;/h3&gt;
&lt;p&gt;If you&apos;ve focused on the prior two points you&apos;re probably in a good place. But the cherry on top is having great error messages. Letting me know at build or runtime what might be wrong will keep me in the development flow as much as possible. Don&apos;t make me hunt for bugs that are rooted in your own proprietary conventions.&lt;/p&gt;
&lt;h3&gt;Good names make for good mental models&lt;/h3&gt;
&lt;p&gt;Finally, zooming out a bit, I think it&apos;s important to track the mental model that your DX paints in developers&apos; minds. One key factor in how that model takes shape is in naming features and conventions in your product. Names will elicit specific contextual connections, and so your product gains to get a leg-up on explaining features by leveraging common developer contexts. Bad names on the other hand do quite the opposite; potentially introducing confusion via mismatched contexts.&lt;/p&gt;
&lt;h2&gt;DX will grow&lt;/h2&gt;
&lt;p&gt;These are just a few stand-out features I find in a good developer experience. But specific focus on DX is fairly new, which will cause the baseline bar to be raised more and more as new techniques emerge. If you&apos;re building a product or service that developers use, it is critical that you adopt and invest in the DX focus.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/productive-developer-experiences.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>To Be Flexible, Be Inflexible</title><link>https://andrewwalpole.com/blog/to-be-flexible-be-inflexible/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/to-be-flexible-be-inflexible/</guid><description>Processes and systems are often built with extreme flexibility in mind, but let&apos;s look at the case for at least considering not generalizing them for the sake of it.</description><pubDate>Wed, 15 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;To Be Flexible, Be Inflexible&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Quick aside: I actually wrote this blog post, &lt;a href=&quot;https://medium.com/nested-loops/in-order-to-be-flexible-be-inflexible-121e6e1dd4d1&quot;&gt;at least one by a similar title&lt;/a&gt;, 10 years ago. Going back and reading it, it didn&apos;t quite hit the mark of what this idea has meant to me since, so I thought I would give it another go.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To lay a quick contextual foundation here, I&apos;m really going to be talking about process. Systems get lumped in here too. But broken down, systems are made up of smaller processes. And I mean all processes; from computer code to project management processes to baking a cake.&lt;/p&gt;
&lt;p&gt;Building flexibility into any process can be incredibly valuable and important when needing to control the capabilities of the outcomes of that process. And in most cases that flexibility is tactically implemented as variable input.&lt;/p&gt;
&lt;p&gt;But flexibility comes at the cost of added complexity. Maybe in software there is a much higher tolerance for allowing systems to be very flexible; especially when the complexity is handled entirely by the computer. However, when that complexity involves people, it quickly acts against the ease and efficiency of executing the process.&lt;/p&gt;
&lt;p&gt;So here&apos;s the pay-off point: &lt;em&gt;to pursue adding flexibility to any given process, you should also explicitly define what is inflexible&lt;/em&gt;; nail down the parts that don&apos;t add as much value by making them variable. Shrink the change and limit the complexity. Doing so will lead to much more balanced, manageable processes, where more focused thoughtfulness can go into the few variable inputs required. This will ultimately generate higher-quality, specialized outcomes without putting undue stress on your operations.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/to-be-flexible-be-inflexible.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Digital Entropy</title><link>https://andrewwalpole.com/blog/digital-entropy/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/digital-entropy/</guid><description>What eats at our code? A quick explorative rambling of why we have to maintain and invest in all code and digital systems.</description><pubDate>Mon, 10 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Digital Entropy&lt;/h1&gt;
&lt;p&gt;A recent &lt;a href=&quot;https://shoptalkshow.com/536/#t=20:23&quot;&gt;Shop Talk Show conversation&lt;/a&gt; around the idea of, &quot;entropy but as it applies to the web&quot; really got my brain gears turning.&lt;/p&gt;
&lt;p&gt;Entropy is defined as:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. a thermodynamic quantity representing the unavailability of a
system&apos;s thermal energy for conversion into mechanical work, often
interpreted as the degree of disorder or randomness in the system.

2. lack of order or predictability; gradual decline into disorder.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entropy as a physics thing already has a huge impact on computing with hardware. The non-software bits are truly affected by our real world and thus can easily succomb to time&apos;s embrace without extreme upkeep.&lt;/p&gt;
&lt;p&gt;But what about software? The idea of digital entropy might not translate super cleanly, but I do see how time and our social constructs (&lt;a href=&quot;https://www.youtube.com/watch?v=ScYgBxLupAs&quot;&gt;some might call those software too&lt;/a&gt;) employ friction upon software systems which will wear them out of their utility and favor if not kept up.&lt;/p&gt;
&lt;h2&gt;Societal Friction&lt;/h2&gt;
&lt;p&gt;I see three big social constructs that grind away at software, and by extension, the websites we build:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Security requirements, functional needs, and user expectations.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;Security requirements&lt;/h3&gt;
&lt;p&gt;When hackers find a new way to exploit our code, we need to patch it, we truly require security to keep our systems safe. There is rarely a week that goes by that there isn&apos;t some new wordpress plugin that needs to be upgraded to avoid zero-day exploits. And the steadiness of that notion has pushed many of us into a higher level of thinking: avoid the server, build static, it&apos;s just safer by default, and thus you reduce the digital entropy inflicted by security over time.&lt;/p&gt;
&lt;h3&gt;Functional needs&lt;/h3&gt;
&lt;p&gt;Functional needs are all about the business tied to the software as a tool; &lt;em&gt;we must keep equitably transacting&lt;/em&gt;, please the share-holders, beat the competition, stay on top. Business requirements change, the strategy and target of conversion changes. This change is often fairly steady, gradual, in the control of the business, moving at a pace that matches its capability. The next version is defined, planned and rolled-out. It&apos;s the natural motion of the social construct of capitalism and it eats away at our code.&lt;/p&gt;
&lt;h3&gt;User expectations&lt;/h3&gt;
&lt;p&gt;Closely related is the ever changing landscape of user expectations. This happens less gradually, and appears more in what we like to call, &lt;em&gt;Innovation&lt;/em&gt;. When someone builds a new way to do something, or a new something all-together that we had not yet exactly realized before and the experience has a disruptive effect on the consumer. It changes minds; &lt;em&gt;your code recodes them&lt;/em&gt;. Goliaths are cut down in favor of the new and novel, maybe permanent, maybe not, but it&apos;s a tumultuous ocean that churns and grates on our code. The ratchet of user expectations clicks into place, squeezing our apps and sites a little tighter, expecting change or inciting death.&lt;/p&gt;
&lt;h2&gt;Beyond Digital Entropy&lt;/h2&gt;
&lt;p&gt;I don&apos;t want to extend the entropy metaphor any further, but I think there is more to the picture especially as we really hone in on the web. Beyond the natural, outside forces that surround the web are the layers of technologies contained within.&lt;/p&gt;
&lt;p&gt;The idea of a development toolchain is not wrong. Our websites have become varying lengths of linked-together technologies, each dependent on the others&apos; strength and capability to continue to produce an end-product. I might also throw out some some sort of mechanical automaton metaphor: all of the determined sprockets and springs, carefully placed, each doing its part. Some sites are constantly on the move, churning; serving. Others, more coin-operated; triggered builds, automated deploy.&lt;/p&gt;
&lt;p&gt;Without maintentance, keeping the machine well-oiled, our links degrade, our gears wear, and the lifespan of our websites is determined by the both the world around and within them.&lt;/p&gt;
&lt;h2&gt;The Lifespan of a Website&lt;/h2&gt;
&lt;p&gt;I bent this blog post intentionally quite a bit, but I really wanted to get here: The lifespan of a website is an interesting idea. I think I&apos;ve clearly laid out all the factors that set the timer in motion. And so the last point to make is that I genuinely believe that &lt;em&gt;the lifespan of a website is exactly tied to two things: how hard it works - how much tech is linked together and how often it conveys - and how often its code is cared for and invested in.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&apos;ll close with a great quote from &lt;a href=&quot;https://twitter.com/livcomp/status/1436802100776689665&quot;&gt;Dave Ackley&lt;/a&gt;, because I think it zooms out on this whole conversation, to a question that I stumbled into, &lt;strong&gt;&quot;Why even bother with this fabricated uphill churning of bits?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There&apos;s one fundamental notion,&amp;lt;br&amp;gt;
Because simplicity is finally best:&amp;lt;br&amp;gt;
Code is data in motion,&amp;lt;br&amp;gt;
Data is code at rest.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because the web, software, digital computation is our new(ish) way of human progress. Data and code, and data and code. It might not feel like it down in the weeds, but these digital systems we dabble in are now deeply tied to how humanity functions and computes as a whole.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/digital-entropy.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Use Vite for JavaScript Libraries</title><link>https://andrewwalpole.com/blog/use-vite-for-javascript-libraries/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/use-vite-for-javascript-libraries/</guid><description>More than once I&apos;ve written some snazzy JavaScript code that I want to quickly turn into a sharable library, but the process of how to easily do that has bogged me down enough to drop the idea entirely. Here&apos;s a look at using vite to quickly publish your code as a JavaScript Library.</description><pubDate>Tue, 07 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Use Vite for JavaScript Libraries&lt;/h1&gt;
&lt;p&gt;More than once I&apos;ve written some snazzy JavaScript code that I want to quickly turn into a sharable library, but the process of how to best and easily do that has bogged me down enough to drop the idea entirely.&lt;/p&gt;
&lt;p&gt;In this post, I&apos;ll walk through how you can build a set of JavaScript into a library provided in both &lt;code&gt;ESM&lt;/code&gt; and &lt;code&gt;UMD&lt;/code&gt; format using vite.&lt;/p&gt;
&lt;p&gt;There&apos;s not a lot of secret sauce here, in fact, most of what I&apos;m about to show you is lifted right from the &lt;a href=&quot;https://vitejs.dev/guide/build.html#library-mode&quot;&gt;vite docs&lt;/a&gt;. But I find it helpful when 3rd-party people can verify a technique.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Starting from a fresh directory we need to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Initialize the project&lt;/li&gt;
&lt;li&gt;Install vite&lt;/li&gt;
&lt;li&gt;Setup some files&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;npm init --yes
npm i vite --save-dev
mkdir lib &amp;amp;&amp;amp; touch lib/main.js vite.config.js index.html 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;lib/main.js&lt;/code&gt; will be your main library entry point that vite will compile and &lt;code&gt;index.html&lt;/code&gt; is actually optional, but what it&apos;s really great for is showing an example of how you use your library, which tends to be a small but nagging pain point in building a library that you want folks to use.&lt;/p&gt;
&lt;h2&gt;Configure&lt;/h2&gt;
&lt;p&gt;Let&apos;s now configure vite to build your library. To do that we need to edit two files: &lt;code&gt;vite.config.js&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;vite.config.js&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const path = require(&apos;path&apos;)
const { defineConfig } = require(&apos;vite&apos;)

module.exports = defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, &apos;lib/main.js&apos;),
      name: &apos;YOUR_LIBRARY_NAME&apos;,
      fileName: (format) =&amp;gt; `YOUR_LIBRARY_NAME.${format}.js`
    }
  }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The magic here is that vite has a &lt;a href=&quot;https://vitejs.dev/guide/build.html#library-mode&quot;&gt;library mode&lt;/a&gt;, and that&apos;s exactly what we&apos;re using. By default it will build your entry point into &lt;code&gt;ESM&lt;/code&gt; and &lt;code&gt;UMD&lt;/code&gt; formats into a &lt;code&gt;/dist&lt;/code&gt; folder.&lt;/p&gt;
&lt;h3&gt;package.json&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;YOUR_LIBRARY_NAME&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;A GOOD DESCRIPTION&quot;,
  &quot;files&quot;:[&quot;dist&quot;],
  &quot;main&quot;:&quot;./dist/YOUR_LIBRARY_NAME.umd.js&quot;,
  &quot;module&quot;:&quot;./dist/YOUR_LIBRARY_NAME.es.js&quot;,
  &quot;exports&quot;: {
    &quot;.&quot;: {
      &quot;import&quot;: &quot;./dist/YOUR_LIBRARY_NAME.es.js&quot;,
      &quot;require&quot;: &quot;./dist/YOUR_LIBRARY_NAME.umd.js&quot;
    }
  },
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;vite&quot;,
    &quot;build&quot;: &quot;vite build&quot;,
    &quot;preview&quot;: &quot;vite preview&quot;
  },
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We wrap-up config by telling your &lt;code&gt;package.json&lt;/code&gt; where your compiled library will live. This is not technically required, but if someone installs your library as a dependency, npm/yarn will know where to pull code from.&lt;/p&gt;
&lt;h2&gt;Write the library&lt;/h2&gt;
&lt;p&gt;Your &lt;code&gt;lib/main.js&lt;/code&gt; file is where you will export all of your code from. If your library is simple you could put all of the code right in there, or if it&apos;s more complex with multiple files, it just needs to import and reexport all the things you want to bundle into the library. It&apos;s also worth noting that if you import external dependencies into your library files they will also get bundled together in the final output.&lt;/p&gt;
&lt;p&gt;As an example, I have recently created &lt;a href=&quot;https://github.com/walpolea/PV&quot;&gt;a wrapper library for petite-vue called PV&lt;/a&gt;. You can check out the library code as well as the &lt;code&gt;index.html&lt;/code&gt; to see how I&apos;m using that to test and build a demo of the library.&lt;/p&gt;
&lt;h2&gt;Build the library&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;npm run build&lt;/code&gt; or &lt;code&gt;vite build&lt;/code&gt; will bundle up your library into &lt;code&gt;/dist&lt;/code&gt; while &lt;code&gt;npm run dev&lt;/code&gt; or &lt;code&gt;vite&lt;/code&gt; will start a server to view your &lt;code&gt;index.html&lt;/code&gt; which makes it easy to test things out as you build the library.&lt;/p&gt;
&lt;p&gt;And that&apos;s all there is to it! If you need to further customize your library, vite is really just exposing a proxy for rollup, and you can add &lt;code&gt;rollupOptions&lt;/code&gt; to your &lt;code&gt;vite.config.js&lt;/code&gt; to customize your build settings, like &lt;a href=&quot;https://github.com/vitejs/vite/discussions/1736#discussioncomment-312982&quot;&gt;multiple entry points&lt;/a&gt;.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/use-vite-for-javascript-libraries.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Always Be Expecting</title><link>https://andrewwalpole.com/blog/always-be-expecting/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/always-be-expecting/</guid><description>Let&apos;s look at how expectations can be used as an explicit device to improve work and working relationships.</description><pubDate>Sat, 25 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Always Be Expecting&lt;/h1&gt;
&lt;p&gt;Expectations are a massively core principle to my own being. I attribute a lot of my success in becoming a leader to the introspection and sharing of expectations. In &lt;a href=&quot;/blog/lead-great-engineering-manager-one-on-ones/&quot;&gt;recounting my one-on-one formula&lt;/a&gt; I talked a lot about setting and resetting them with your direct reports; doing so builds trust and provides clarity.&lt;/p&gt;
&lt;p&gt;But well beyond being a tool to strengthen working relationships, constant self-reflection on expectations and alignment across business partners and project collaborators can increase productivity and the success of work.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What should I expect?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&apos;s it. Form a habit of asking this question.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Before starting a new project: How will it go? What should I expect to be difficult?&lt;/li&gt;
&lt;li&gt;When tackling a new task: How long do I expect this to take? What are others&apos; expectations around how this should be done?&lt;/li&gt;
&lt;li&gt;In meetings: What are we all expecting to get out of this discussion?&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What do you expect?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Just as important as it is to exercise your explicit internal conversations around expectations, external discussions around what others think are essential to aligning on what needs to be done next. It allows us to measure the differences between multiple perspectives, and most importantly unify ideas and desired outcomes before pressured conflict forces it out.&lt;/p&gt;
&lt;p&gt;Clarifying your own and others&apos; expectations is a solid tactic to address upcoming unknowns and tackle work productively and confidently. And while you might not always fully reveal the fog of war ahead, it will give you tangible readiness to face things as they come, as surprising or unsurprising as they might be.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/always-be-expecting.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Three Design Smells Only Developers Can Sense</title><link>https://andrewwalpole.com/blog/three-design-smells-only-developers-can-sense/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/three-design-smells-only-developers-can-sense/</guid><description>In development, we have &quot;code smells.&quot; Extending the metaphor, here are some design smells that devs can pick up on as well.</description><pubDate>Wed, 01 Mar 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Three Design Smells Only Developers Can Sense&lt;/h1&gt;
&lt;h2&gt;Using the font-size hack&lt;/h2&gt;
&lt;p&gt;Ever seen or done this?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;html { font-size: 62.5%; }
body { font-size: 1.6rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s the classic font-size hack, where instead of the default &lt;code&gt;1rem = 16px&lt;/code&gt;, the equation is forced to &lt;code&gt;1rem = 10px&lt;/code&gt; allowing for easier math and translation from &lt;code&gt;px&lt;/code&gt; to &lt;code&gt;rem&lt;/code&gt; font-sizes.&lt;/p&gt;
&lt;p&gt;There is some debate and confusion over whether this is bad to do. Technically no: it doesn&apos;t negatively affect the user experience. Fonts can still scale with zoom.&lt;/p&gt;
&lt;p&gt;As a deviation from web defaults though, it&apos;s arguably unnecessarily added system complexity that impacts DX over UX, which is not a super strong position to be taking.&lt;/p&gt;
&lt;p&gt;But what&apos;s the underlying issue? Well, I think it&apos;s rooted in font sizes traditionally starting in pixels on the design side.&lt;/p&gt;
&lt;p&gt;And so, I propose that it&apos;s a &lt;em&gt;Design Smell&lt;/em&gt;, a metaphorical extension of &lt;em&gt;Code Smell&lt;/em&gt; where code is a symptom of another development decision. In this case, that &quot;other decision&quot; is landing back a few steps in the design phase.&lt;/p&gt;
&lt;p&gt;Teaching web designers how preferred web font size definitions work, especially as tools like Figma adopt them, would be a great way to tackle this issue at its root.&lt;/p&gt;
&lt;p&gt;What other Design Smells might there be?&lt;/p&gt;
&lt;h2&gt;Unoptimized media&lt;/h2&gt;
&lt;p&gt;How media is handed over for development can be fraught with odor. Dev tools and lighthouse tests can easily point out that large non-web-optimized media is present. But beyond that, things like using a &lt;code&gt;jpg&lt;/code&gt; or &lt;code&gt;png&lt;/code&gt; over an &lt;code&gt;svg&lt;/code&gt; just because there&apos;s a gradient background offers the chance to coach back into design-land that CSS is great at gradients and there&apos;s a better solution to improve both performance and quality of that design element.&lt;/p&gt;
&lt;h2&gt;Responsive gotchas&lt;/h2&gt;
&lt;p&gt;Another category of design smells exist around responsive design. While there are many specific instances to call out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Navigations not accounting for more or fewer items.&lt;/li&gt;
&lt;li&gt;Grids of cards designed with the same &lt;code&gt;lorem ipsum&lt;/code&gt; content across all of them.&lt;/li&gt;
&lt;li&gt;Heights of mobile components perfectly fit into a single viewport size.&lt;/li&gt;
&lt;li&gt;Ambiguous resizing guidance in extra large viewports.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of these come down to not exploring the edge cases of responsive circumstances, especially those that throw in the &lt;em&gt;wrench of real content&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I have to say, that if these make it unscathed into development, you&apos;ll be holding your nose all the way through. Ambiguous responsive design is especially important to tackle before development starts, which is why design and development collaboration is so important.&lt;/p&gt;
&lt;h2&gt;Follow your nose&lt;/h2&gt;
&lt;p&gt;I think &lt;em&gt;Design Smell&lt;/em&gt; can be a useful term, especially in bridging the developer-designer relationship. As the capabilities of the web, especially CSS, grow, it&apos;s imperative to maintain an open, two-way avenue for knowledge transfer. Just as front-end developers can learn to exercise design skills to fill in gaps and not get tripped up, web designers too will yield better UX design because of knowing how the web works.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/three-design-smells-only-developers-can-sense.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>An Introduction To Robust-First Computation</title><link>https://andrewwalpole.com/blog/an-introduction-to-robust-first-computation/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/an-introduction-to-robust-first-computation/</guid><description>Did you know there&apos;s an entire field of computer science barely yet explored? Join me at the entrance to a deep rabbit hole as we take a look at Robust-First Computation.</description><pubDate>Thu, 09 Mar 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;An Introduction To Robust-First Computation&lt;/h1&gt;
&lt;p&gt;In 2017, with an hour to kill before teaching a web development class at UCSD, I stumbled into a free talk being put on. &lt;a href=&quot;https://www.youtube.com/watch?v=Dmlm6mtnSZs&quot;&gt;&lt;em&gt;Living Computation &amp;amp; Postdeterministic Digital Design&lt;/em&gt; by Dave Ackley&lt;/a&gt;. Little did I know it was to be an inflection point in my entire view of computer science and personal scientific interests.&lt;/p&gt;
&lt;p&gt;This post is likely to get long, so I&apos;ll split up the sections into a table of contents for easy reference:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#beyond-hardware-determinism&quot;&gt;A World Beyond Hardware Determinism&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#understanding-robust-first&quot;&gt;Understanding Robust-First&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#new-world-of-computer-science&quot;&gt;A Whole New World of Computer Science&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#living-computation-as-robust-first-code&quot;&gt;Living Computation as Robust-First Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-movable-feast-machine&quot;&gt;The Movable Feast Machine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mfms&quot;&gt;MFMS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-t2-tile-project&quot;&gt;The T2 Tile Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#mfm-js&quot;&gt;MFM-JS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#get-involved&quot;&gt;Get Involved&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;beyond-hardware-determinism&quot; href=&quot;#beyond-hardware-determinism&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;A World Beyond Hardware Determinism&lt;/h2&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;To set the stage for all of this, we have to look at the foundational components of computing we desperately rely on today. CPU and RAM; &lt;a href=&quot;https://en.wikipedia.org/wiki/Von_Neumann_architecture&quot;&gt;the Von Neumann Architecture&lt;/a&gt; has been the underlying fixture upon which we compute for nearly 80 years. It&apos;s the guarantee of hardware determinism that allows us to code in the way we do: the minutely micromanaged dictatorship of the &lt;em&gt;Centralized&lt;/em&gt; Processor Unit tells us that with its complete worldview and direct access to the entire software state held in RAM, it will not ever deviate from perfection. So that from step one of our program to step one billion, we can rely on deterministic results and values to be what we expect them to be.&lt;/p&gt;
&lt;p&gt;This is how Dave put it in his talk, and when put this way it sounds quite amazing, &lt;em&gt;too amazing&lt;/em&gt;. Hardware determinism is fragile. Sure it has taken us incredibly far, I&apos;m not saying to discount it at all. But the fact is, we&apos;ve climbed so high on this fragile path that as we begin to see limitations ahead of us, we&apos;re becoming more and more aware of the treacherous place we&apos;re currently computing in.&lt;/p&gt;
&lt;p&gt;One bit-flip and we might be cooked - might not be too - but might be indeed, and there&apos;s no way to predict which it will be. Crashes, blue screens, all of these symptoms showing through the cracks of hardware determinism. And we pave over them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Just one more bug to fix!&quot;&lt;/li&gt;
&lt;li&gt;&quot;Try turning it off and on again, it&apos;s efficient enough!&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We avoid the underlying issue entirely because we accept it absolutely as the immovable, unchangeable part of the system.&lt;/p&gt;
&lt;h4&gt;But here&apos;s the mind-blowing, blue-pill-offering moment:&lt;/h4&gt;
&lt;p&gt;What of a world beyond hardware determinism? A new system, built from the ground up, where hardware certainly tries to be correct, but doesn&apos;t guarantee it, and without that contract, software now has to be built to run and adapt in that world.&lt;/p&gt;
&lt;p&gt;Well, that&apos;s &lt;em&gt;Robust-First Computation&lt;/em&gt;, a computational system of hardware and software that embraces non-deterministic execution, and it&apos;s not just an abstract idea anymore.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;understanding-robust-first&quot; href=&quot;#understanding-robust-first&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;Understanding &quot;Robust-First&quot;&lt;/h2&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;In order to understand why it&apos;s Robust-First Computation, we need to break the problem down. CPU and RAM impose a C.E.O. paradigm into its computational policy: &lt;em&gt;Correct and Efficient Only!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As soon as we dispose of that foundation, we need to understand what other concepts then become available and useful on this less reliable platform. Robustness is &lt;em&gt;the ability to withstand or overcome adverse conditions or rigorous testing&lt;/em&gt; and in an environment that is no longer guaranteed to be correct, lends itself incredibly well as a concept to deal with incorrectness or unknown conditions. So as we introduce the ability to be incorrect, we can trade off efficiency for robustness (because they are at odds) in order to maintain correctness.&lt;/p&gt;
&lt;p&gt;And so we&apos;re left with the Robust-First Computing Creed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;First be robust&amp;lt;br&amp;gt;
Then as correct as possible&amp;lt;br&amp;gt;
Then as efficient as necessary&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;new-world-of-computer-science&quot; href=&quot;#new-world-of-computer-science&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;A Whole New World of Computer Science&lt;/h2&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;Hello. Are you still with me? Thanks! Let&apos;s try to bridge into the more tangible aspects of these wild ideas I&apos;ve laid out, and get into what this all might look like from a hardware and software perspective.&lt;/p&gt;
&lt;p&gt;It&apos;s important to set a good perspective for all of this. Undoing CPU and RAM and starting over is hard and will be slow, and we&apos;re only barely beyond, or even arguably at, the starting line. &lt;em&gt;But that&apos;s what is so interesting to me.&lt;/em&gt; After 80 years of core computer science discovery, nearly all based on deterministic execution, this feels like a small, obscured basement door, left dusty and unperturbed, that opens up into an entirely new parallel universe of undiscovered computer science. &lt;strong&gt;Absolutely wild!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;living-computation-as-robust-first-code&quot; href=&quot;#living-computation-as-robust-first-code&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;Living Computation as Robust-First Code&lt;/h2&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;So what does robust-first code look like? One way to answer this question objectively is to look around and see if we can find any robust-first systems that already exist, and in fact, they do! Humans, animals, microorganisms, plants; &lt;em&gt;living systems&lt;/em&gt; are incredible examples of computational systems built to withstand and execute amidst wild amounts of varying environmental factors.&lt;/p&gt;
&lt;p&gt;From Dave&apos;s own &lt;a href=&quot;http://robust.cs.unm.edu/doku.php?id=introduction:robust-first_computing&quot;&gt;Robust-first computing wiki&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Viewed as a kind of computer, note how different a living organism is compared to a serial deterministic machine. Deterministic machines are 100% completely repeatable – from the same inputs will come the exact same outputs — while living organisms rarely do anything the exact same way twice. Deterministic machines will crash, seize-up, or otherwise misbehave when virtually anything goes wrong inside them; living organisms, by contrast, can suffer grievous injury and yet survive, handle the immediate situation, and get away long enough to heal up and live on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And so in thinking about what our code might look like, it&apos;s put forth that living systems are ripe for the picking to discover some of the base principles that might help us get into this new world.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Wound_healing&quot;&gt;healing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Gene_redundancy&quot;&gt;redundancy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Phenotypic_plasticity&quot;&gt;plasticity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Stigmergy&quot;&gt;stigmergy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design&quot;&gt;bottom-up computing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Multi-agent_system&quot;&gt;agential systems&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And many more observable concepts can be repurposed into the pursuit of building robust software.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;But Andrew, that&apos;s not writing code, that&apos;s thinking about writing code!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Too true! To get down to the clickity-clacking of our keyboards there&apos;s a lot of work to figure out and do. But once again, thanks to Dave Ackley and others thinking about these ideas for many years now, there are some places to lean on to jump further into it.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;the-movable-feast-machine&quot; href=&quot;#the-movable-feast-machine&quot;&amp;gt;&lt;/p&gt;
&lt;h3&gt;The Movable Feast Machine&lt;/h3&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;The Movable Feast Machine (&lt;a href=&quot;https://github.com/DaveAckley/MFM&quot;&gt;MFM&lt;/a&gt;) is a specific architectural implementation of a software operating system, meant as a basis for programming robust-first software. Certainly, it&apos;s one of many possible ways you could build a robust-first architecture, but having worked with it myself, I would implore you to take a deep look at all it has to offer before trying to come up with your own system.&lt;/p&gt;
&lt;p&gt;&amp;lt;video width=&quot;100%&quot; style=&quot;aspect-ratio:16/9;&quot; src=&quot;/static/blog/robust/mfm.mp4&quot; autoplay loop muted&amp;gt;&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;When people first see the MFM running, it&apos;s common to hear, &quot;oh, it&apos;s like Conway&apos;s Game of Life!&quot; And the answer is, &quot;Yes, a little bit, but &apos;No&apos; a lot a bit.&quot;&lt;/p&gt;
&lt;p&gt;The MFM is a spatially laid-out 2D &lt;code&gt;Grid&lt;/code&gt;, where individual grid &lt;code&gt;Sites&lt;/code&gt;, implemented on hardware &lt;code&gt;Tiles&lt;/code&gt; can contain &lt;code&gt;Atoms&lt;/code&gt; which are essentially small agent programs that execute code or behaviors upon the grid within their own localized space. The important part is that there is no central control here. Each Atom on the grid can only see Manhattan Distance 4 sites away from its current location. This is called the &lt;code&gt;Event Window&lt;/code&gt; and it brings enormous power and constraint in building atoms to navigate and do things together on the grid.&lt;/p&gt;
&lt;p&gt;&amp;lt;img style=&quot;width:max(320px, 50%);&quot; src=&quot;/static/blog/robust/event-window.png&quot; alt=&quot;The Event Window&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;The tiles, where sites are implemented are treated as an implementation detail in the architecture: they&apos;re required to create a physical grid of tiles, and do help with robustness in that tiles can be swapped in and out as things need maintenance, but otherwise, the sites and atoms have no understanding that they are there; there is only the vast grid.&lt;/p&gt;
&lt;p&gt;This brings us to an aside that I had not yet figured out how to work in. The MFM grid is built to be &lt;em&gt;indefinitely scalable&lt;/em&gt;. Don&apos;t get it confused with infinitely scalable, we&apos;re not talking theory here. By having no centralized hardware or software unit, the MFM is designed to surpass traditional computing power, not by being efficient and fast, but by being able to grow in size without limits.&lt;/p&gt;
&lt;p&gt;The official MFM is currently available in two places:&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;mfms&quot; href=&quot;#mfms&quot;&amp;gt;&lt;/p&gt;
&lt;h3&gt;MFMS&lt;/h3&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;First, you can get started with the Movable Feast Machine Simulator (MFMS). Dave has built this as a software program that runs on Linux and can be programmed in two programming languages he has concocted for robust-first programming: &lt;a href=&quot;https://github.com/DaveAckley/ULAM&quot;&gt;ULAM&lt;/a&gt; and &lt;a href=&quot;https://github.com/DaveAckley/SPLAT&quot;&gt;SPLAT&lt;/a&gt;. The &lt;a href=&quot;https://github.com/elenasa/ULAM/wiki&quot;&gt;ULAM wiki&lt;/a&gt; is a great place to start if you want to take a look.&lt;/p&gt;
&lt;p&gt;Boiled down, programming an atom is a matter of defining how it should behave using what it can see (the Event Window) and the limited available state it has, to manipulate its local environment. Randomly, an atom will be picked to run, and execute that behavior.&lt;/p&gt;
&lt;p&gt;Constantly churning, random execution across the grid then creates a system where these atoms can compose their behaviors to work together to build larger systems, bottom-up, to force robust-first thinking into your software architecture.&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;the-t2-tile-project&quot; href=&quot;#the-t2-tile-project&quot;&amp;gt;&lt;/p&gt;
&lt;h3&gt;The T2 Tile project&lt;/h3&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;Second, you can follow along with Dave himself as he has undergone a public, youtube-chronicled, multi-year journey of going from simulator to full hardware implementation. &lt;a href=&quot;https://t2tile.com&quot;&gt;The T2 Tile project&lt;/a&gt; documents the journey of building physical hardware tiles that run a true version of the MFM architecture. To date, the project has been quite a success in proving-out the system and uncovering problems in building this type of architecture.&lt;/p&gt;
&lt;p&gt;The T2 Tile Project introduction video is also a great way to hear a lot of these high-level concepts that I&apos;ve been talking about in a clearer, more concise way.&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; style=&quot;aspect-ratio:16/9;&quot; src=&quot;https://www.youtube.com/embed/jreRFxN6wuM&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;Or if you want to skip all the way forward to seeing what this indefinitely scalable tiled grid of computers looks like, there&apos;s a &lt;a href=&quot;https://www.youtube.com/channel/UCvYU9hl3y-anHrD_Z6ECB_Q&quot;&gt;T2 Tile Demos&lt;/a&gt; channel as well.&lt;/p&gt;
&lt;p&gt;&amp;lt;img style=&quot;width:max(320px, 50%);&quot; src=&quot;/static/blog/robust/t2tiles.jpg&quot; alt=&quot;Grid of actual T2 Tiles on a wall&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;mfm-js&quot; href=&quot;#mfm-js&quot;&amp;gt;&lt;/p&gt;
&lt;h3&gt;MFM-JS&lt;/h3&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;If you&apos;re reading this because you follow me, you&apos;re likely a lot more web-oriented in focus. Being a fanatical follower of this movement for many years, I have concocted my own implementation of the &lt;a href=&quot;https://github.com/walpolea/MFM-JS&quot;&gt;Movable Feast Machine as a JavaScript library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Leaving that talk in 2017, an unquenchable desire to see what robust-first programming was all about started to grow in me. Without the ability to run the MFMS, my only option was to reimplement the concepts for myself to a level that let me play with it all first-hand.&lt;/p&gt;
&lt;p&gt;Out of that came &lt;a href=&quot;https://mfm.rocks&quot;&gt;MFM.rocks&lt;/a&gt; where you can see some of the Elements I&apos;ve built over the years in action. And this &lt;a href=&quot;https://twitter.com/walpolea/status/1055483881950834688?s=20&quot;&gt;twitter thread&lt;/a&gt; follows some of the highlights, learnings and experiments I undertook as well.&lt;/p&gt;
&lt;p&gt;&amp;lt;div style=&quot;display:grid;grid-template-columns:1fr 1fr;gap:10px;&quot;&amp;gt;
&amp;lt;video width=&quot;100%&quot; style=&quot;aspect-ratio:1/1;&quot; src=&quot;/static/blog/robust/cell-split.mp4&quot; autoplay loop muted&amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;video width=&quot;100%&quot; style=&quot;aspect-ratio:1/1;&quot; src=&quot;/static/blog/robust/trap.mp4&quot; autoplay loop muted&amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;I&apos;m happy to walk through the interworkings of the MFM-JS engine and Element programming API if anyone is interested. Just pop into the T2 Tile discord (linked below) and send a message!&lt;/p&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;a id=&quot;get-involved&quot; href=&quot;#get-involved&quot;&amp;gt;&lt;/p&gt;
&lt;h2&gt;Get Involved&lt;/h2&gt;
&lt;p&gt;&amp;lt;/a&amp;gt;&lt;/p&gt;
&lt;p&gt;If you made it this far, thank you again. It&apos;s a lot to digest and took me a while to connect many of the dots.&lt;/p&gt;
&lt;p&gt;I&apos;ve been wanting to put this all together for a while now, but to what end? Well, I guess part of me thinks there are folks like me who will be very intrigued by these ideas and want to explore them further as I have. If that might be you, I hope this offers enough getting-started material for you to begin the descent into the rabbit hole.&lt;/p&gt;
&lt;p&gt;Here are some additional useful links to help your exploration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Join us in the &lt;a href=&quot;https://discord.gg/rBV6Y6sWNY&quot;&gt;T2 Tile Discord&lt;/a&gt; where further questions and discussions about all this stuff is encouraged!&lt;/li&gt;
&lt;li&gt;Dave also runs a non-profit called the &lt;a href=&quot;https://www.livingcomputation.org/&quot;&gt;Living Computation Foundation&lt;/a&gt; where you can donate to the T2 Tile project.&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href=&quot;https://www.youtube.com/channel/UC1M91QuLZfCzHjBMEKvIc-A&quot;&gt;T2 Tile Youtube Channel&lt;/a&gt; and the &lt;a href=&quot;https://www.youtube.com/@DaveAckley&quot;&gt;Dave Ackley Youtube channel&lt;/a&gt; which has some great MFM demos and teachings.&lt;/li&gt;
&lt;li&gt;Here&apos;s an &lt;a href=&quot;https://www.livingcomputation.org/edu/alife2020/&quot;&gt;ALife 2020 workshop&lt;/a&gt; that walks you through setting up MFMS and some ULAM and SPLAT basics.&lt;/li&gt;
&lt;li&gt;More Information and links about the MFM can be found at &lt;a href=&quot;https://movablefeastmachine.com/&quot;&gt;movablefeastmachine.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Dave has written one of my favorite &quot;computer-science fiction&quot; pieces called, &lt;a href=&quot;https://zenodo.org/record/1304010#.ZAlK6uzMJqs&quot;&gt;The Path to Best Effort&lt;/a&gt; which is a scientific publication written in the year 2039 and chronicles the historical uprising of robust-first computational systems.&lt;/li&gt;
&lt;li&gt;You can find &lt;a href=&quot;https://hachyderm.io/@livcomp&quot;&gt;Dave&lt;/a&gt; and &lt;a href=&quot;https://mastodon.online/@walpolea&quot;&gt;I&lt;/a&gt; on mastodon as well.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mastodon.social/@TodePond&quot;&gt;Lu Wilson&lt;/a&gt; also has a lovely MFM-inspired engine called &lt;a href=&quot;https://sandpond.cool/&quot;&gt;Sandpond&lt;/a&gt; which they talk about on their &lt;a href=&quot;https://www.youtube.com/@TodePond&quot;&gt;Youtube Channel&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Even Von Neumann himself thought we would &lt;a href=&quot;https://www.lesswrong.com/posts/Eve2miBH8wAhhxNwT/von-neumann-s-critique-of-automata-theory-and-logic-in&quot;&gt;be moving away from Serial Determinism&lt;/a&gt; very soon after his initial architecture was demonstrated.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/an-introduction-to-robust-first-computation.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Are We The Weirdos?</title><link>https://andrewwalpole.com/blog/are-we-the-weirdos/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/are-we-the-weirdos/</guid><description>Only in the last few years have I realized that web dev as a profession and a hobby is like having a superpower, but it also makes me kind of weird.</description><pubDate>Tue, 18 Apr 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Are We The Weirdos?&lt;/h1&gt;
&lt;p&gt;To put the ending first, &lt;em&gt;yes&lt;/em&gt;! I think if you&apos;re here reading this on your own time, because you&apos;re in web dev mastodon or (ugh) birdsite, or from a discord server, or you have me in your RSS reader, or did a nerdy google search: &lt;em&gt;yes, we are the weirdos&lt;/em&gt;. But it&apos;s really a great kind of weird.&lt;/p&gt;
&lt;p&gt;I only came to this realization a handful of years ago. I&apos;ll admit, for most of my early career I thought everyone obsessed over the latest and greatest HTML, CSS and JavaScript happenings, I thought it just came with the job. But it turns out, there&apos;s just a pretty good bubble for that sort of thing; perhaps mostly grown via blogging and web dev Twitter, all the folks engaged enough to hit publish early and often have created a euphoric echo chamber, that I&apos;m very content to get comfy in.&lt;/p&gt;
&lt;p&gt;Anyway, the point? Once I started realizing that it&apos;s more of a minority that spends some sub-section of their free time doing web dev stuff, the more I realized how much of an advantage we all have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There&apos;s less frustration encountered as the needs of your job often trail the knowledge of your passion.&lt;/li&gt;
&lt;li&gt;And related, it becomes easy to lead the conversation at work when you can be confident of how things are evolving.&lt;/li&gt;
&lt;li&gt;Having the additional exposed context to the wide breadth of the web ecosystem comes in handy constantly, allowing for better system design and thoughtful solutions.&lt;/li&gt;
&lt;li&gt;Being engaged in the community, especially on Discords as I have found, gives you a great resource and platform beyond work walls to gut-check your thoughts and ideas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Are there downsides?&lt;/h2&gt;
&lt;p&gt;Yes, I think so. Blurring the line between work and passion can be precarious to manage. I think it&apos;s important to build up some semblance of a wall to make sure you&apos;re pursuing development as a passion in a way that is not work, and instead, still does pique your interest. Related, checking in on yourself to make sure you&apos;re free time endeavors are not additive to burn-out feelings is critical.&lt;/p&gt;
&lt;h2&gt;It&apos;s really a spectrum&lt;/h2&gt;
&lt;p&gt;I&apos;m not a fan of binary divisions, so I think this is all a weirdo spectrum of sorts. I&apos;ve come across lots of co-workers that genuinely have deep passion for their work, but they just happen to love doing other fun things in their free time. And I&apos;ve met a few that truly just see it as a job, keeping their skills just as sharp as they need to carry out their responsibilities.&lt;/p&gt;
&lt;p&gt;But I really identify with the folks that engage and obsess over it all. &lt;a href=&quot;https://chriscoyier.net&quot;&gt;Chris Coyier&lt;/a&gt; touched on it on the &lt;a href=&quot;https://shoptalkshow.com/561/&quot;&gt;ShopTalk Show podcast&lt;/a&gt; recently, saying,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;I always thought that was a cheat code I had for life. It just so happens that something I find relatively entertaining is this kind of thing, not necessarily listening to our show, but I listen to lots of different shows and blogs and stuff, and I read it and do all my RSSing and YouTubing and all that stuff. There&apos;s some negative implications to this too, but as a hobby almost, like I actually kind of find it fun. I say it&apos;s a cheat code because it becomes useful then, like when something comes up I&apos;m like &lt;em&gt;&apos;oh yeah that&apos;s the HSTS lists built into chrome browsers...&apos;&lt;/em&gt; and the only reason I know that is because I read something about it one time almost recreationally, not because of my career experience, but because my hobby helped me get there.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And he&apos;s absolutely right! I don&apos;t think there&apos;s a week that goes by where that extra bit of staying connected to the web development world as a hobby doesn&apos;t help me out with being productive at work.&lt;/p&gt;
&lt;p&gt;So if you&apos;re a weirdo like me, I say keep being weird; it&apos;s okay to love what you do professionally to a point where it takes up your leisure time; it&apos;s not something you have to fight if you don&apos;t want to. And that&apos;s quite the final point I want to emphasize: I spent a decent bit of time not allowing myself to be comfortable with this idea, but once I embraced it and said, &quot;hey, it&apos;s great,&quot; it only made things better.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/are-we-the-weirdos.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>The Web Is Moving at an Unprecedented Pace</title><link>https://andrewwalpole.com/blog/the-web-is-moving-at-an-unprecedented-pace/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/the-web-is-moving-at-an-unprecedented-pace/</guid><description>The idea that the web is evolving quickly is obvious, but needs pointing out. Here I reflect on how that pace is starting to have an impact on teams that haven&apos;t kept up.</description><pubDate>Wed, 12 Jul 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;The Web Is Moving at an Unprecedented Pace&lt;/h1&gt;
&lt;p&gt;That word, &quot;unprecedented&quot; is a bit loaded. I mean it less as sensationalism and more that it&apos;s an important attribute of how the web is evolving today. Because it may mean that we and our teams need to act in ways that we never have before.&lt;/p&gt;
&lt;h2&gt;Why are things moving so quickly now?&lt;/h2&gt;
&lt;p&gt;Hindsight now affords us a clear view that many factors have contributed to the acceleration of web tech over the last ~5 years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;React, Vue and other frameworks matured as reliable application-level technologies which translated into more growth for the use of our core web technologies.&lt;/li&gt;
&lt;li&gt;Communities grew and rallied across social avenues, providing constructive discourse, data, thought-leadership and volunteered action, which trickled directly into the various web spec teams having the person-power and motivation to triage ideas and grow our spec feature-sets.&lt;/li&gt;
&lt;li&gt;Browsers too reacted to the pace picking up and through internal efforts sped up their own release cycles and aligned with each other via the &lt;a href=&quot;https://web.dev/interop-2022/&quot;&gt;interop&lt;/a&gt; project.&lt;/li&gt;
&lt;li&gt;And while there are more specific items to add to this list, I think overall, the momentum built to a point where progress now begets progress, as making the web platform more capable and productive is hitting real bottom lines.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;What does this fast pace mean?&lt;/h2&gt;
&lt;p&gt;I want to cut to the chase: I see the cracks starting to show in teams and projects that haven&apos;t kept up; through various open-source projects, to friends and friends of friends struggling to keep their stress levels down due to tech debt coming to collect. It&apos;s visibly accelerating; more and more deprecated, even abandoned dependencies, projects stuck on incredibly old and now brittle node versions, Dependabot shouting daily about new security vulnerabilities.&lt;/p&gt;
&lt;p&gt;Back to things being unprecedented, a lot of web developers kept a mostly even pace set from the early 2000&apos;s to around 2015, which was fairly methodical, not necessarily stagnant, but pretty manageable to deal with the odd new feature that trickled down the pipe. And I think until now, the repercussions of keeping that pace haven&apos;t yet critically surfaced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So here&apos;s my warning:&lt;/strong&gt; The old skill sets need updating, right away. The gap is widening between those who are engaged with the change and those who fell into a comfort zone of what they know. Things will only get worse as the new productivity of the web leaves old concepts further in the dust.&lt;/p&gt;
&lt;h2&gt;Business needs to get involved!&lt;/h2&gt;
&lt;p&gt;Individually, if this idea resonates with you I think that&apos;s great, the power is absolutely in your own hands. But my bigger concern is with teams; whole companies even. How development teams are managed provides the biggest opportunity to catch up. Time needs to be invested and accounted for in keeping pace with the industry. It&apos;s no longer going to work to offer outdated solutions that miss the mark of being a part of today&apos;s modern web. Without strategic investment in updating role/skill matrices and individual learning plans, we could see many teams and companies succumb to overwhelming technical debt caused by the accelerated rising tide of &lt;a href=&quot;/blog/digital-entropy&quot;&gt;digital entropy&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;There&apos;s opportunity&lt;/h2&gt;
&lt;p&gt;The good news is, I don&apos;t think it&apos;s too late. With the right adjustments and investments any person, team or organization can catch up. And not only that, but the opportunity here isn&apos;t just to catch up, it&apos;s about continuing to keep pace in this marathon of a time. Doing that and also building awareness around how the industry continues to move will provide incredible confidence to the software and products you build.&lt;/p&gt;
&lt;h2&gt;Adjust and get moving&lt;/h2&gt;
&lt;p&gt;It&apos;s a common trend that as technology matures, it becomes more imperative that businesses understand clearly how to utilize it rather than rely on techno-mystique to get them by; ultimately time with tech leads to the savvy winning-out.&lt;/p&gt;
&lt;p&gt;But the abrupt adjustment to the evergreen nature of the web today is disruptive to the idea of this pattern happening in longer-term cycles. Those who haven&apos;t yet, or don&apos;t soon, tighten that loop will start to fall out of sync entirely, and those that do it well will stand to gain quite a bit.&lt;/p&gt;
&lt;p&gt;Whether it&apos;s front-end, back-end, devops or other interfacing skill-arenas that power the web ecosystem, the optimal concepts to deploy those domains have changed drastically in the last few years and will continue to move as fast or faster. If you&apos;re feeling left behind, now might be a great, and unprecedented, time to adjust and get moving.&lt;/p&gt;
&lt;h2&gt;Not sure how to take the first steps?&lt;/h2&gt;
&lt;p&gt;The great news is that beyond hobby bloggers like me, there are some incredible voices out there making it really easy to get exposed to and learn some of the new cool concepts emerging in web tech. Here is just a glimpse of bloggers and articles that can get you on your way.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://chriscoyier.net/2023/06/06/modern-css-in-real-life/&quot;&gt;Modern CSS in Real Life by Chris Coyier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;And how about a double-feature: &lt;a href=&quot;https://css-tricks.com/the-web-is-good-now/&quot;&gt;The Web is Good Now by Chris Coyier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://io.google/2023/program/0ac6834a-9ed1-4145-ad6e-2b23c02239b8/&quot;&gt;What&apos;s new in web UI by Una Kravets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bram.us/2021/12/21/the-css-has-selector-is-way-more-than-a-parent-selector/&quot;&gt;The CSS :has() selector is way more than a “Parent Selector” by Bramus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cssgrid.io/&quot;&gt;CSSGrid.io by Wes Bos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://daverupert.com/2023/05/getting-started-view-transitions/&quot;&gt;Getting started with View Transitions on multi-page apps by Dave Rupert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/almanac/properties/a/aspect-ratio/&quot;&gt;aspect-ratio by Geoff Graham&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://buildexcellentwebsit.es/&quot;&gt;buildexcellentwebsit.es by Andy Bell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Better yet, skip the links and join the communities. Across discord, mastodon, bluesky, twitter (meh, sort of), and now threads, there are a ton of folks out there to connect with and learn from in real time.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/the-web-is-moving-at-an-unprecedented-pace.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>CSS Grid Systems with CSS Grid</title><link>https://andrewwalpole.com/blog/css-grid-is-too-good/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/css-grid-is-too-good/</guid><description>Let&apos;s talk about why there aren&apos;t any.</description><pubDate>Wed, 30 Aug 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;CSS Grid Systems with CSS Grid&lt;/h1&gt;
&lt;p&gt;It&apos;s funny. If you google around for a modern CSS Grid framework that uses CSS Grid, you won&apos;t find much. You will see very few new frameworks at all, but you will find many that have thrived over the last decade or more, a lot of which are bundled together with larger component libraries or web scaffolding systems.&lt;/p&gt;
&lt;p&gt;Most of those frameworks use a traditional &lt;code&gt;n-columns&lt;/code&gt; (usually defaulting to 12) grid layout system made popular by Bootstrap or Foundation. And for good reason: these patterns were web development staples for a long time. Even with the advent of Flexbox and it not being a 1-to-1 replacement of those, these grid systems required a hefty layer of CSS on top to make them go &lt;code&gt;brrrrrr&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Enter CSS Grid&lt;/h2&gt;
&lt;p&gt;But then in 2017, all the major modern browsers (excluding IE) got support for CSS Grid. I remember it coming out of the gate with a pretty big bang, especially with &lt;a href=&quot;https://front-end.social/@jensimmons&quot;&gt;Jen Simmons&lt;/a&gt; and Mozilla pouring time and resources into evangelizing Grid&apos;s capabilities and Firefox&apos;s novel and superior devtools support of it.&lt;/p&gt;
&lt;p&gt;The early Grid days were good; buzz about it lingered, and it seemed like a lot of folks adopted it. But eventually, like all new shiny features, Grid became just another thing to reach for.&lt;/p&gt;
&lt;p&gt;It&apos;s at this point that I will fast-forward to six years later of hindsight to look back on: &lt;em&gt;A lot of folks missed CSS Grid!&lt;/em&gt; I&apos;ve talked with dozens of devs over the last few years who don&apos;t reach for it at all or don&apos;t even know it is something to be reached for.&lt;/p&gt;
&lt;p&gt;And I get it. Similarly, I missed Flexbox, having shirked a lot of front-end learning in place of doing Flash and Actionscript during that time. It wasn&apos;t until only a few years ago that I really started building up my muscle memory for Flexbox.&lt;/p&gt;
&lt;p&gt;So now it makes sense why these older style grid frameworks are still thriving. People learned the API, they work well; the patterns are comfortable and well-established.&lt;/p&gt;
&lt;h2&gt;Exit CSS Grid&lt;/h2&gt;
&lt;p&gt;But here&apos;s my big insight: &lt;strong&gt;People missed CSS Grid a little extra because it&apos;s so good and capable on its own, you don&apos;t need to build a framework around it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And so all that hot new software that we saw with wrapping Flexbox or float layouts over many years; all that hype and buzz the communities would be full of at regular intervals wasn&apos;t there for Grid. Only now that browsers are finally rallying to get us subgrid is there a little resurgence in buzz.&lt;/p&gt;
&lt;h2&gt;Grid Doesn&apos;t Need a Framework&lt;/h2&gt;
&lt;p&gt;So here&apos;s my buzz, and my testimony as a CSS Grid addict: &lt;em&gt;Grid makes building for the web very unscary, or dare I say, easy.&lt;/em&gt; I reach for it daily. And I&apos;ve even &lt;a href=&quot;https://codepen.io/walpolea/pen/ExOKZVZ?editors=0100&quot;&gt;tried to build a framework around it&lt;/a&gt;. But you don&apos;t really need it. Its syntax is lightweight and immensely capable on its own, for both simple straight-forward web development up through highly componentized modular systems.&lt;/p&gt;
&lt;p&gt;I encourage anyone who has not given it a decent go to put in the time to make it a tool in your tool belt.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Hidden customText Preheaders in Veeva/Salesforce Emails</title><link>https://andrewwalpole.com/blog/hidden-customtext-preheader-in-veeva-salesforce-emails/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/hidden-customtext-preheader-in-veeva-salesforce-emails/</guid><description>Once thought to be an impossible ask, here&apos;s a simple workaround to allow customText hidden preheaders in Veeva and Salesforce emails.</description><pubDate>Sun, 03 Sep 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Hidden customText Preheaders in Veeva / Salesforce Emails&lt;/h1&gt;
&lt;p&gt;Here&apos;s a very specific one in hopes of preventing someone from having the fraught Google expedition I had.&lt;/p&gt;
&lt;p&gt;My job has me moderately learning and working in Veeva for email development. This is essentially a document-based CMS that lets you stitch parts and pieces of files and code together to build fairly complex customizable emails and other experiences. One feature is the &lt;code&gt;customText&lt;/code&gt; directive which allows you to inline multiple string choices within any set of text. Something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{customText[Here is a custom option|And this is a another custom option|And one more for good measure]}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Useful!&lt;/p&gt;
&lt;p&gt;Another email-specific concept relevant to this story is Preheaders. Many email clients take the first few sentences of text in an email and append it after the subject in their interfaces so the user has a little more context as to what the email is about. Given that, it&apos;s a little bit of a marketing hack to create this idea of Preheaders, or hidden text that comes first in the body copy of the email so that the email clients show specifically chosen text rather than the actual starting text of the email message. Sneaky!&lt;/p&gt;
&lt;p&gt;A set of preheader code might look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span class=&quot;preheader&quot; style=&quot;font-size:0px;line-height:0px;max-height:0px;max-width:0px;mso-hide:all !important;overflow:hidden;visibility:hidden;display:none;&quot;&amp;gt;Hey this is my preheader text&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; with a bunch of inlined ways to make it visually disappear from various email clients.&lt;/p&gt;
&lt;h2&gt;The Problem&lt;/h2&gt;
&lt;p&gt;Ok, now that we&apos;re up to speed, what if we want to combine these two things: &lt;code&gt;customText&lt;/code&gt; hidden preheaders? Well you can&apos;t do it, at least that&apos;s what the internet has to say about it. What&apos;s the issue? When you load an email from Veeva into Salesforce it sees the &lt;code&gt;customText&lt;/code&gt; directive and proceeds to replace it with a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; dropdown of your options. The only problem is that it renders within the email markup that the &lt;code&gt;customText&lt;/code&gt; lives in, so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span class=&quot;preheader&quot; style=&quot;font-size:0px;line-height:0px;max-height:0px;max-width:0px;mso-hide:all !important;overflow:hidden;visibility:hidden;display:none;&quot;&amp;gt;
  {{customText[This is the first optional hidden preheader|And this is a second optional hidden preheader]}}
&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the Salesforce email editor becomes something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span class=&quot;preheader&quot; style=&quot;font-size:0px;line-height:0px;max-height:0px;max-width:0px;mso-hide:all !important;overflow:hidden;visibility:hidden;display:none;&quot;&amp;gt;
  
  &amp;lt;!--  Salesforce adds in this &amp;lt;select&amp;gt; for the choices:  --&amp;gt;
  &amp;lt;span class=&quot;AE_customText&quot; tagtext=&quot;customText&quot; required=&quot;false&quot;&amp;gt;
    &amp;lt;select id=&quot;0&quot; class=&quot;hideCustomTextValues&quot;&amp;gt;
      &amp;lt;option id=&quot;0_0&quot; value=&quot;This is the first optional hidden preheader&quot;&amp;gt;This is the first optional hidden preheader&amp;lt;/option&amp;gt;
      &amp;lt;option id=&quot;0_1&quot; value=&quot;This is the first optional hidden preheader&quot; style=&quot;display: none&quot; hidden=&quot;&quot;&amp;gt;This is the first optional hidden preheader&amp;lt;/option&amp;gt;
      &amp;lt;option id=&quot;0_2&quot; value=&quot;And this is a second optional hidden preheader&quot;&amp;gt;And this is a second optional hidden preheader&amp;lt;/option&amp;gt;
      &amp;lt;option id=&quot;0_3&quot; value=&quot;And this is a second optional hidden preheader&quot; style=&quot;display: none&quot; hidden=&quot;&quot;&amp;gt;And this is a second optional hidden preheader&amp;lt;/option&amp;gt;
    &amp;lt;/select&amp;gt;
  &amp;lt;/span&amp;gt;
  
&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;See the problem? The &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; is within your hidden preheader markup, so as you might expect, it doesn&apos;t render the dropdown in Salesforce!&lt;/p&gt;
&lt;h2&gt;The Solution&lt;/h2&gt;
&lt;p&gt;So how can we fix this? We need to show the dropdown in Salesforce but continue to hide it in our final rendered emails. Enter the fairly new CSS &lt;code&gt;:has()&lt;/code&gt;!&lt;/p&gt;
&lt;p&gt;We can select the &lt;code&gt;.preheader&lt;/code&gt; class by looking to see if it contains Saleforce&apos;s &lt;code&gt;.AE_customText&lt;/code&gt; class, and undo all the hiddenness of the preheaders, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@supports selector(:has(*)) {
  .preheader:has(.AE_customText) {
    display:block !important;
    opacity:1 !important;
    visibility:visible !important;
    overflow:unset !important;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Salesforce will now show the dropdown so you can make a choice, but when the email is rendered anywhere else the &lt;code&gt;.AE_customText&lt;/code&gt; class won&apos;t exist and none will be the wiser!&lt;/p&gt;
&lt;p&gt;Here&apos;s a codepen of the full solution:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;jOXMVJE&quot; data-user=&quot;walpolea&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/walpolea/pen/jOXMVJE&quot;&amp;gt;
Email css for salesforce optional preheaders&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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 is:inline async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Remote Leadership Takes a Concerted Effort</title><link>https://andrewwalpole.com/blog/remote-leadership-takes-a-concerted-effort/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/remote-leadership-takes-a-concerted-effort/</guid><description>Three years on since many companies went remote and some leaders still struggle with leading remotely. Here&apos;s what I think it takes.</description><pubDate>Sat, 09 Sep 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Remote Leadership Takes a Concerted Effort&lt;/h1&gt;
&lt;p&gt;Not that good leadership in general doesn&apos;t take calculated skill, but if we&apos;re looking at the huge pandemic-driven swing from few to many leaders becoming remote workers, it&apos;s worth talking about how an absence of physical presence impacts how leaders need to execute on their role to be successful.&lt;/p&gt;
&lt;h2&gt;It&apos;s all about interfaces&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;image-float-left&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/static/blog/nokia-phone.png&quot; alt=&quot;An illustration of an old Nokia phone&quot; width=&quot;200px&quot; style=&quot;width:200px;padding:20px 10px 0px 0;&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;The kinetic work of leadership is mostly about interfacing with people. The abrupt move from in-person to going remote seems akin to replacing your keyboard and mouse with your old Nokia phone keypad. It&apos;s not that you&apos;re left entirely dead in the water, I mean if you are old enough to have used one of those devices you&apos;ll know we got pretty adept at playing snake and composing full sentences with 10 buttons. But certainly, it&apos;s an initial major hit to productivity if you don&apos;t make the specific effort to adapt.&lt;/p&gt;
&lt;p&gt;It&apos;s at this point that I can&apos;t help but recall the beginning of the pandemic. For myself, I had not worked remotely full-time before, but I think I was lucky for a few reasons: First, being an engineering manager, there were fewer obstacles to overcome being distanced between screens when the work me and my team were doing was already so natively available on those screens. Second, my tactical approach to leadership helped me very quickly understand that those same tactics had to be translated and adapted to remote work.&lt;/p&gt;
&lt;p&gt;The big apparent insight in breaking down the situation was that remote work meant a steep decline in natural opportunities to interface directly with people.&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;Concerted is the Key&lt;/h2&gt;
&lt;p&gt;It&apos;s really the exact word I want to press upon in my point here: &lt;em&gt;Concerted means to jointly plan or coordinate.&lt;/em&gt; And so effective remote leadership means a leader being deliberate in creating opportunities to meet with their folks, but also to arrange and do it in a way that works for all parties. Truly an effort of communicating to communicate more is needed: &lt;em&gt;Let&apos;s talk about work, but first, let&apos;s talk about how we talk about work.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Taking this extra step fills a void that physical spaces innately provide, from the position of your desk to theirs, or the sizes and availabilities of meeting spaces; their physical fixedness guide us to fall into a communication pattern and cadence that just works. Having the deliberate conversation that figuratively breaks the fourth wall of your computer screen helps to break down the actual digital barriers between leader and led.&lt;/p&gt;
&lt;p&gt;So if you are a leader, or even just being led remotely, and you feel like that loss of physical space has not been accounted for, I think these thoughts can provide a solid basis for how you can begin to fill the gap to establish stronger leadership ties. Embrace the meta of it and have real conversations about how to have better work conversations.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Glowing Blurred Backgrounds with CSS</title><link>https://andrewwalpole.com/blog/glowing-blurred-backgrounds-with-css/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/glowing-blurred-backgrounds-with-css/</guid><description>A quick walk-through of an easy, artsy and impressive pure CSS visual effect.</description><pubDate>Sun, 10 Sep 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Glowing Blurred Backgrounds with CSS&lt;/h1&gt;
&lt;p&gt;About a year ago I was asked to create a &lt;a href=&quot;https://codepen.io/walpolea/pen/ZERZOaB&quot;&gt;glowing ball of light animated background effect&lt;/a&gt; for a website. The end result was very cool looking for being so easy to pull off with just CSS. Here&apos;s a quick look at how it&apos;s done.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;clip-path: polygon()&lt;/code&gt; and &lt;code&gt;filter: blur()&lt;/code&gt; on a container you can create some pretty cool and performant glowing, animating background effects:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;525&quot; data-default-tab=&quot;css,result&quot; data-slug-hash=&quot;JjwYaxM&quot; data-user=&quot;walpolea&quot; style=&quot;height: 525px; 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/walpolea/pen/JjwYaxM&quot;&amp;gt;
Pretty Blurred Backgrounds with clip-path and filter:blur&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;&lt;/p&gt;
&lt;p&gt;I think the code is pretty straightforward, so I won&apos;t go line-by-line, but here are the things to note:&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;clip-path:polygon()&lt;/code&gt; is a really great way to create interesting and scalable shapes since you can use percentage-based values. On top of that, they are pretty easy to create with &lt;a href=&quot;https://bennettfeely.com/clippy/&quot;&gt;online tools&lt;/a&gt; or vector graphics software.&lt;/p&gt;
&lt;p&gt;I happen to prefer Adobe Illustrator, and while it&apos;s paid, I&apos;m well-versed with it and I love that I can copy a path out of it directly into &lt;code&gt;vscode&lt;/code&gt; as an SVG. And pro-tip, your SVG export settings in Illustrator will match how that copy/pasted code looks, so you can fine-tune it to format exactly as you like. Illustrator does prefer to export SVGs with absolute pixel values, so the other trick there is to use a &lt;code&gt;100px x 100px&lt;/code&gt; artboard to draw your path, and then use &lt;a href=&quot;https://codepen.io/walpolea/pen/MWZaReg&quot;&gt;some JavaScript like this&lt;/a&gt; to convert the path from pixel to percentage values! Works like a charm!&lt;/p&gt;
&lt;p&gt;Now back to the CSS effect! Since we&apos;re going to be blurring these paths, they don&apos;t have to be perfect at all. In fact, I get away with turning three rough blob shapes into a single path by connecting them with a sliver of path that ends up disappearing in the blurring process.&lt;/p&gt;
&lt;p&gt;Here it is without the blurring applied:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;525&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;jOXBpZj&quot; data-user=&quot;walpolea&quot; style=&quot;height: 525px; 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/walpolea/pen/jOXBpZj&quot;&amp;gt;
Pretty Blurred Backgrounds with clip-path and filter:blur&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;&lt;/p&gt;
&lt;p&gt;The next tip is to animate the internal shapes, but apply the &lt;code&gt;filter:blur()&lt;/code&gt; to the container. This will give you the performance you want on an effect like this. It also helps to blur with a &lt;code&gt;vw&lt;/code&gt; unit since a fixed value might not be as blurred as you want on large screens or too blurred on small screens.&lt;/p&gt;
&lt;p&gt;Lastly, you can use gradients and &lt;code&gt;mix-blend-mode&lt;/code&gt; to get the exact effect you&apos;re looking for. The little CSS framework I&apos;ve provided in the above code also gives you all you need to layer multiple paths on top of each other and tweak values like colors, opacity, size, position, and speed.&lt;/p&gt;
&lt;p&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;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>If You Blog, Consider Blogcasting</title><link>https://andrewwalpole.com/blog/if-you-blog-consider-blogcasting/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/if-you-blog-consider-blogcasting/</guid><description>As someone who has been wanting to do a podcast but not having time for it. Blogcasting seems like a great middle-ground solution.</description><pubDate>Sat, 11 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;If You Blog, Consider Blogcasting&lt;/h1&gt;
&lt;p&gt;I had the not-so-new idea of attaching an audio companion to my blog. As someone who sort of wants to dip into podcasting (beyond &lt;a href=&quot;https://spotifyanchor-web.app.link/e/WA1kAZiJjxb&quot;&gt;the one I already tried&lt;/a&gt;), but doesn&apos;t quite have the time or drive to get something going right now, it seemed like a cool compromise.&lt;/p&gt;
&lt;p&gt;I&apos;m calling it blogcasting, because it seems right, and certainly it&apos;s not a term I invented, but as long as I don&apos;t google it, I can claim to have.&lt;/p&gt;
&lt;h2&gt;How to get started Blogcasting&lt;/h2&gt;
&lt;p&gt;I went down a few avenues to get the blogcast up and running. First I thought I would use a podcasting platform to host it, and then, bonus, it would be a podcast too. Ultimately though, most podcast hosts are not free and it&apos;s a bigger process to add in all the metadata for a podcast episode just to get something published.&lt;/p&gt;
&lt;p&gt;Thanks to friends in the &lt;a href=&quot;https://www.patreon.com/shoptalkshow/posts&quot;&gt;ShopTalk Show d-d-d-discord&lt;/a&gt; though, I was able to figure out a simpler implementation.&lt;/p&gt;
&lt;h3&gt;Step 1: Record the episode&lt;/h3&gt;
&lt;p&gt;First, I settled on a very quick recording process using audio hijack to capture the audio, and Adobe Audition to do a very quick listen-through and editing of pace and mistakes. So far each episode takes about 1.5x its length to edit, and that seems pretty good.&lt;/p&gt;
&lt;h3&gt;Step 2: Host the episode&lt;/h3&gt;
&lt;p&gt;You need a place to host your &lt;code&gt;.mp3&lt;/code&gt; files. In some cases your blog might have a server that can do this. In my case, a static blog hosted on Cloudflare, I chose to create a Cloudflare R2 storage bucket to host my files.&lt;/p&gt;
&lt;h3&gt;Step 3: Implement the blogcast into your blog posts&lt;/h3&gt;
&lt;p&gt;Since I&apos;m now using Astro, I was able to very quickly create a &lt;code&gt;BlogcastPlayer.astro&lt;/code&gt; component that uses &lt;a href=&quot;https://github.com/muxinc/media-chrome&quot;&gt;&lt;code&gt;muxinc/media-chrome&lt;/code&gt;&lt;/a&gt; under the hood. It&apos;s a really fantastic web component library for doing quick custom video and audio players.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;BlogcastPlayer.astro&lt;/code&gt; component looks something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
const {url} = Astro.props;
---

&amp;lt;div class=&quot;blogcast-player&quot; &amp;gt;
  &amp;lt;media-controller audio&amp;gt;
    &amp;lt;audio
    slot=&quot;media&quot;
    src={url}
    &amp;gt;&amp;lt;/audio&amp;gt;
    &amp;lt;media-control-bar&amp;gt;
      &amp;lt;media-play-button&amp;gt;&amp;lt;/media-play-button&amp;gt;
      &amp;lt;media-time-range&amp;gt;&amp;lt;/media-time-range&amp;gt;
      &amp;lt;media-playback-rate-button&amp;gt;&amp;lt;/media-playback-rate-button&amp;gt;
      &amp;lt;media-time-display show-duration&amp;gt;&amp;lt;/media-time-display&amp;gt;
      &amp;lt;media-mute-button&amp;gt;&amp;lt;/media-mute-button&amp;gt;
      &amp;lt;media-volume-range&amp;gt;&amp;lt;/media-volume-range&amp;gt;
    &amp;lt;/media-control-bar&amp;gt;
  &amp;lt;/media-controller&amp;gt;
  &amp;lt;p&amp;gt;Listen along to this post as you read, or &amp;lt;a href=&quot;https://andrewwalpole.com/feed.xml&quot;&amp;gt;subscribe to the blog&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
  import &apos;media-chrome&apos;;
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given a &lt;code&gt;url&lt;/code&gt; prop, the player renders itself. Not shown are some of the extra styles I applied to fix it to the bottom of the page, but hopefully you get the gist of it.&lt;/p&gt;
&lt;h3&gt;Step 4: Add the media to your RSS feed&lt;/h3&gt;
&lt;p&gt;This is honestly the most surprising and cool part about this. I had no clue that all you need to do to add media to accompany a blog post feed is to add an &lt;code&gt;&amp;lt;enclosure /&amp;gt;&lt;/code&gt; tag pointing to the file for each &lt;code&gt;&amp;lt;item&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It might look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;enclosure url=&quot;https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/if-you-blog-consider-blogcasting.mp3&quot; type=&quot;audio/mp3&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most RSS readers are happy to pick this up and display a playable interface.&lt;/p&gt;
&lt;h2&gt;Will you give it a shot?&lt;/h2&gt;
&lt;p&gt;What do you think? Is this an appealing idea? Have you tried it out on this post? I&apos;d love to hear your feedback, whether it&apos;s just dropping a like in the lower right (sorry RSS folks), or replying to my &lt;a href=&quot;https://twitter.com/walpolea/status/1624206212937191424?s=20&amp;amp;t=yuD8VWXotnPgkJSKT6JGPQ&quot;&gt;tweet&lt;/a&gt; or &lt;a href=&quot;https://mastodon.online/@walpolea/109843310354666047&quot;&gt;toot&lt;/a&gt; sharing this post. I myself am looking forward to Blogcasts sweeping the nation!&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/if-you-blog-consider-blogcasting.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Servers Got Hot So Front-End Got Cool</title><link>https://andrewwalpole.com/blog/servers-got-hot-so-front-end-got-cool/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/servers-got-hot-so-front-end-got-cool/</guid><description>2023 marked the crescendo of the return of server-based web tech, and with it came a greater industrial appreciation of front-of-the-front-end skills.</description><pubDate>Thu, 21 Dec 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Servers Got Hot So Front-End Got Cool&lt;/h1&gt;
&lt;p&gt;As I reflect back on the year in web development, what stands out to me is that the steady comeback of servers (especially via serverless) in web architecture, which has been steadily mounting for the past many years, seems to have finally hit an eruption point. Specifically SSR and Hybrid SSR (that&apos;s SSR + SSG intermingled) are now top architectural contenders for delivering websites large and small, simple and complex, as the natural scalability of complexity these concepts can handle is elegant and efficient.&lt;/p&gt;
&lt;p&gt;On top of that, buy-in solidified at the platform level, both in modern web hosting and frameworks. Where folks were once fine trading-off some hoop-jumping to cobble SSR into their sites, it&apos;s now a first-class switch in many circumstances to enable an exact balance of performance and capability across any site or app.&lt;/p&gt;
&lt;p&gt;Furthermore, I&apos;ll dare to make a connection here that because server-side popularity has grown, it has somewhat pulled the back-of-the-front-end more towards back-end, leaving a clearer gap between front-of-the-front-end needs. Or said differently, I think appreciation is growing for mastery in HTML and CSS as complex JavaScripty logic leans heavier into back-end territory. It also helps that CSS is also having its moment as the CSSWG is hitting its stride in proposing and growing the language at a pace unseen in years past.&lt;/p&gt;
&lt;p&gt;So servers got hot and front-end got cool, recovering from a bit of a bludgeoning from the javascript-all-the-things crowd as they also got put in their place a bit. But will it last? There are a lot of architectural fads that tend to rise and fall in our industry, and while I&apos;m sure we&apos;ll continue to see newer concepts overtake old, as a renewed resurgence and the fact that it&apos;s over the hump of being a one-size-fits-all solution, I can easily imagine Hybrid SSR lasting us through the next decade of web development, acting as a capable base allowing us to focus on innovation in other areas.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Bloxels is the Best STEAM Software for Kids</title><link>https://andrewwalpole.com/blog/bloxels-is-the-best-steam-software-for-kids/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/bloxels-is-the-best-steam-software-for-kids/</guid><description>No sponsors here, just an honest look back and review of a valuable and endlessly fun educational game for kids.</description><pubDate>Sat, 13 Jan 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Bloxels is the Best STEAM Software for Kids&lt;/h1&gt;
&lt;p&gt;In December of 2020, my then seven year-old was big into art and Angry Birds, which was his first foray into video games. He would play the game and when his screen time was over switch to his desk to draw all of the characters and the levels. As an engineering dad seeing his brain begin to piece together the complexities of playing a game like that, I naturally began itching to find a great gift that could introduce him to programming.&lt;/p&gt;
&lt;p&gt;We had tried a few things previously; like those robots you can program to move with simple arrow buttons to denote going forward or turning. They were neat, but didn&apos;t really capture his attention. A bit of the same with scratch and scratch-like visual programming tools and games; He could understand the basics, but there wasn&apos;t enough payoff to them that held his attention.&lt;/p&gt;
&lt;p&gt;Looking at what else might be out there to fill the &quot;engineering for kids&quot; void, it seemed as if I had to concede that there really wasn&apos;t a great product for such an early age.&lt;/p&gt;
&lt;h2&gt;Enter Bloxels&lt;/h2&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Deciding to pivot on engineering a tiny bit, I purchased &lt;a href=&quot;https://bloxels.com&quot;&gt;Bloxels&lt;/a&gt;, an educational video game building app. I recall being quite unsure about it as there were a lot of mixed reviews on Amazon. It was an interesting product though. Not only were you purchasing a voucher to have an account and own the app, it also came with a physical pixel art board that lets kids design characters and levels by placing colored cubes into a grid; novel, but a neat way to turn pixel art into a tangible event.&lt;/p&gt;
&lt;p&gt;So on Christmas morning, my little one opened it up and with a clearly excited-confused look of, &quot;Wow, amazing... what is it?!?!&quot; our journey into Bloxels began.&lt;/p&gt;
&lt;h2&gt;What Bloxels is and what it isn&apos;t&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Before I go on, I just want to note that one of the great things about Bloxels is that what it is now is much more evolved from what it was three years ago. So from here on out I&apos;ll be referring to the latest capabilities rather than just the old.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Bloxels is not really a programming game. At least not in a clear one-to-one manner. You won&apos;t find an exposed programming language with if statements and loops and variables to learn. Instead, Bloxels focuses more on the mechanics of design, both in form and function. And so in that sense, Bloxels is absolutely an &lt;em&gt;engineering&lt;/em&gt; game.&lt;/p&gt;
&lt;h3&gt;Teaching the form of design&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Bloxels is a great first drawing app for kids. I could even imagine that, had I been a Bloxels user earlier, I would have plopped my three or four year old in front of the character designer to play around with building pixel art. You can build pixel-based sprites with an easy to use interface and even animate them. It&apos;s an incredible introduction to digital art that balances ease of use and complexity of capability with masterful UX design.&lt;/p&gt;
&lt;p&gt;There are essentially four modes of design within Bloxels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The character builder (for building characters with all their moving states)&lt;/li&gt;
&lt;li&gt;The art builder (for building environmental level tiles and items)&lt;/li&gt;
&lt;li&gt;A background builder (for creating backgrounds for your levels to sit on top of)&lt;/li&gt;
&lt;li&gt;And the game builder itself (for designing and bringing all your assets together into a game)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first three design modes mentioned above have a very similar drawing app interface; it&apos;s the place you can really exercise those artistic pixel-art skills. The game builder though, is quite another beast.&lt;/p&gt;
&lt;h3&gt;Teaching the function of design&lt;/h3&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Upon creating a new game, you&apos;re given a choice: &lt;code&gt;Platformer&lt;/code&gt; or &lt;code&gt;Top-Down&lt;/code&gt;; think original Mario Bros. vs original The Legend of Zelda. Once you make a choice the game builder sets you into your level, which is a large gridded-out canvas where you can place blocks. Different &lt;a href=&quot;https://www.bloxels.com/tutorials/8-block-types&quot;&gt;block types&lt;/a&gt; make up the core functionality of building games; from &lt;strong&gt;terrain&lt;/strong&gt;, &lt;strong&gt;hazard&lt;/strong&gt;, &lt;strong&gt;collectible&lt;/strong&gt;, &lt;strong&gt;story block&lt;/strong&gt;, &lt;strong&gt;enemy&lt;/strong&gt;, &lt;strong&gt;action block&lt;/strong&gt;, &lt;strong&gt;power-up&lt;/strong&gt; and &lt;strong&gt;liquid&lt;/strong&gt;, you have everything you need to design out the function of the game. The thing I love most about this is that the types of blocks are color-coded and can be placed down completely from a functional perspective first, and then later skinned with your various block-art designs. This presents a huge engineering skill packed into a very simple feature: planning out your functional design and mechanics separate from focusing on your artistic vision.&lt;/p&gt;
&lt;p&gt;Each block type also has configuration settings, this is where we start to see some programming concepts in the game. Though limited to the features of Bloxels, there are enough if/then scenarios that can be built and toggled to do a decent job of building the mental foundations of connecting various decision trees to form a functional game.&lt;/p&gt;
&lt;p&gt;You can set up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Doors and keys&lt;/li&gt;
&lt;li&gt;Various difficulties and types of enemies&lt;/li&gt;
&lt;li&gt;Power-ups for your character to change their abilities mid-game&lt;/li&gt;
&lt;li&gt;Warp points to move throughout levels and even across separate game maps&lt;/li&gt;
&lt;li&gt;And much more; Bloxels continues to add features regularly to keep games engaging&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Set up for success and encouraging failure&lt;/h3&gt;
&lt;p&gt;The last thing I&apos;ll focus on is how the game comes with a ton of asset packs and pre-made examples for kids to get started from. This creates a smooth on-ramp of usability and learning that is often lacking in software. At the same time, the &lt;code&gt;test&lt;/code&gt; button is almost always on screen, encouraging learning to experiment and iterate with ideas with a near real-time feedback loop.&lt;/p&gt;
&lt;h3&gt;They doubled down on the engineering&lt;/h3&gt;
&lt;p&gt;I&apos;m not going to belabor convincing you that this is a good app for kids; &lt;strong&gt;it really is&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead, let&apos;s take a look at what really clinches Bloxels for me personally as something I &lt;em&gt;want&lt;/em&gt; to support. Remember when I mentioned the bad Amazon reviews? Well it turns out that Bloxels original owners didn&apos;t do a great job at marketing the product and began failing as a company. Therefore they went through a period where they were offloading product with pretty much zero support, causing folks with issues to run screaming to the review section to say what a scam it was.&lt;/p&gt;
&lt;p&gt;I&apos;m not sure I have the story pieced together 100% correct, but it seems like one of the lead engineers was able to rescue the brand and the app from the company, take it over and turn the ship around by pouring time into making it a better, more reliable product. Years later, Bloxles seems to be quite healthy, and their continuous investment into features over many years has made it the incredible app it is today.&lt;/p&gt;
&lt;p&gt;Under the hood, &lt;a href=&quot;https://play.bloxels.com&quot;&gt;play.bloxels.com&lt;/a&gt; is a pretty elegant looking SPA. All of the various features, capabilities and textures come in over API endpoints as structured JSON data, which is then rendered client-side. Given that once you&apos;re editing games nearly everything you see is user-generated or user-configured, it&apos;s quite a smart tactic to save and load sets of configurations back and forth, with efficiencies going into the size and structures of those datasets. Just open up &lt;code&gt;devtools&lt;/code&gt; as you poke around and you&apos;ll be impressed with the interworkings of fetch calls and responses.&lt;/p&gt;
&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;We&apos;ve been a &lt;a href=&quot;https://bloxels.com&quot;&gt;Bloxels&lt;/a&gt; household for over three years now and there are few apps or games that I can recall having such a long-term reverence for. I thought I was just getting something fun for my son to express his creativity and was surprised to find out it absolutely was that and a ton more. Bloxels strikes a balance of fun and education in a way that is quite rare. It promotes learning to iterate on design and take into consideration the player and advanced game mechanics and shows how bringing function and aesthetic design together is a powerful combination for creating engaging experiences. It is the best STEAM app I&apos;ve come across for young children to learn how all the aspects of design and engineering play a powerful role in our world.&lt;/p&gt;
&lt;p&gt;Once again, these thoughts are my own entirely, Bloxels has no understanding that I&apos;m publishing this. They have just proven themselves deserving of kind and reverent words and a solid recommendation from this engineering dad.&lt;/p&gt;
&lt;p&gt;I&apos;ll leave you with one of my son&apos;s first games to try out for yourself: &lt;a href=&quot;https://play.bloxels.com/arcade/33646494&quot;&gt;Flopsy&apos;s Adventure&lt;/a&gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>AI Now is like The Jetsons</title><link>https://andrewwalpole.com/blog/ai-now-is-like-the-jetsons/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/ai-now-is-like-the-jetsons/</guid><description>A short thought on AI and its progression and promises for the future.</description><pubDate>Mon, 04 Mar 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;AI Now is like The Jetsons&lt;/h1&gt;
&lt;p&gt;That is to say, if I were to place down a bet on all the grand futures we&apos;re currently imagining and reading about with the AI revolution now infantly upon us, probably a lot of what is predicted to come, in broad strokes, will eventually be so, but most of how and when it will materialize is not.&lt;/p&gt;
&lt;p&gt;Dreams of flying cars, robot maids and video telecommuting, just a few of many ideas that are finally becoming sort of true. But 60 years later? Hope your internet connection is stable during all those zoom calls. Oh no, what did your roomba just smear across the floor? And have you seen our flying cars?!? I think it&apos;s still going to take a bit.&lt;/p&gt;
&lt;p&gt;Not that it will be another 60 years for things to really get moving, we &lt;em&gt;are&lt;/em&gt; moving a &lt;em&gt;lot&lt;/em&gt; faster. But we&apos;ve still got a lot of iterating, legislating, appealing, regressing, forging, considering, and all those other messy things that wind the path for individuals and teams and departments and companies and societies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The point?&lt;/strong&gt; I think it&apos;s two things that I think of often amidst the daily sensations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&apos;t be ignorant of history, while the path may be new, we are the same wayfarers.&lt;/li&gt;
&lt;li&gt;There will be a place for everyone and time for everyone to find their place.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Use @vue/reactivity for Reactive Data Stores</title><link>https://andrewwalpole.com/blog/use-vue-reactivity-for-reactive-data-stores/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/use-vue-reactivity-for-reactive-data-stores/</guid><description>vue/reactivity is a small, stand-alone reactivity system that any JavaScript framework can use. Here are three ways to create shared state stores with it.</description><pubDate>Sat, 27 Jan 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Use @vue/​reactivity for Reactive Data Stores&lt;/h1&gt;
&lt;p&gt;Vue&apos;s reactivity system has been around since 2020, but I&apos;ve often thought it has gone under the radar. That thought was more than confirmed for me when the idea of signals dropped last year and everyone seemed to go nuts at it&apos;s revelational reactive state capabilities. To me, at both first glance and deeper-dive, signals was barely different from &lt;code&gt;@vue/reactivity&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But I can see why it has been overlooked. It has &lt;code&gt;vue&lt;/code&gt; in the name, so it must be for Vue! That&apos;s not the case. Though built for and into the core offering of &lt;code&gt;Vue 3&lt;/code&gt;, the &lt;code&gt;@vue/reactivity&lt;/code&gt; package stands alone just fine and can be used completely absent of Vue the component framework.&lt;/p&gt;
&lt;p&gt;Again for those of you in the back:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vuejs/core/tree/main/packages/reactivity&quot;&gt;&lt;code&gt;@vue/reactivity&lt;/code&gt;&lt;/a&gt; is a completely stand-alone and light-weight reactivity system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Why reactive data stores?&lt;/h2&gt;
&lt;p&gt;There are a lot of patterns that vary in complexity to add the capability of shared state across an application or website. Without getting in the weeds there, the reactivity part of a reactive data store pattern is really about abstracting away the ability for state to become updated as it changes across the places it needs to be used. So instead of having the manually retrieve values as they update, you have automatic mechanisms to tap into performing actions as state updates.&lt;/p&gt;
&lt;h2&gt;3 Ways to create reactive stores&lt;/h2&gt;
&lt;p&gt;One thing I like about the &lt;code&gt;@vue/reactivity&lt;/code&gt; package is that you have a few options as to how you want to structure your stores. You can level up the complexity of them which helps you build in safeguards needed to wrangle state across larger apps, or keep them dead simple for smaller needs. Let&apos;s take a look at three patterns, going from simple to more complex:&lt;/p&gt;
&lt;h3&gt;Really simple global store&lt;/h3&gt;
&lt;p&gt;This is about as simple as it gets. We use the library&apos;s &lt;code&gt;reactive&lt;/code&gt; object to store all of our values, getters and actions. This is great for small but highly shared sets of data that need to stay in sync.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;store.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { reactive, computed } from &apos;@vue/reactivity&apos;;

// global reactive state
export const state = reactive({
  count: 0,
  increment() {
    state.count++;
  },
  countIsEven: computed( () =&amp;gt; state.count % 2 === 0 ),
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we want to use the store, we simply import the &lt;code&gt;store&lt;/code&gt; object and access all the properties. Here&apos;s an example of wiring-up our counter with vanilla &lt;code&gt;HTML&lt;/code&gt; and &lt;code&gt;javascript&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;somewhere-else.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;count&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;div class=&quot;is-even&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;button class=&quot;inc&quot;&amp;gt;++&amp;lt;/button&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;
import { state } from &apos;./store.js&apos;;
import { effect } from &quot;@vue/reactivity&quot;;

document.querySelector(&apos;.inc&apos;).addEventListener(&apos;click&apos;, () =&amp;gt; state.increment() );

effect( () =&amp;gt; {
  document.querySelector(&apos;.count&apos;).innerHTML = state.count;
  document.querySelector(&apos;.is-even&apos;).innerHTML = state.countIsEven;
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This introduces the &lt;code&gt;effect&lt;/code&gt; function, which is the way we tap into state changing automatically: any function passed into &lt;code&gt;effect()&lt;/code&gt; will be run when the state changes.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/walpolea/pen/ExMXKOQ?editors=1010&quot;&gt;View as Codepen Demo&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;The flexible store&lt;/h3&gt;
&lt;p&gt;This pattern ups the complexity a bit but provides a few safeguards and lets you distinguish between global shared state and also instance-specific local state values.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;useCounter.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import { ref, computed } from &apos;@vue/reactivity&apos;;

// Global state, getters, actions:
const globalCount = ref(0);

const countIsEven = computed( () =&amp;gt; globalCount.value % 2 === 0 );

function incrementGlobalCountOnly() {
  globalCount.value++;
}

//Scoping our store into a function allows us to capture
//local state which can be run across multiple instances of useCounter
export const useCounter = () =&amp;gt; {

  // Local state, getters and actions
  const localCount = ref(0);

  const localCountIsEven = computed( () =&amp;gt; localCount.value % 2 === 0 );

  function increment() {
    globalCount.value++;
    localCount.value++;
  }

  return {
    globalCount, 
    countIsEven,
    incrementGlobalCountOnly,
    localCount,
    increment,
    localCountIsEven
  };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we&apos;ve established two places to declare state and actions. Outside of the &lt;code&gt;useCounter&lt;/code&gt; function, these values will be shared among all users of the store, but declarations made within the function provide each caller of &lt;code&gt;useCounter()&lt;/code&gt; its own localized copy of that value. This allows for a reusable pattern that lets you compose state and function into any sort of UI; excellent for components!&lt;/p&gt;
&lt;p&gt;Here&apos;s a look at using this counter store with templating library, &lt;a href=&quot;https://lit.dev/docs/v1/lit-html/introduction/&quot;&gt;&lt;code&gt;lit-html&lt;/code&gt;&lt;/a&gt;, where we put together a quick reusable &lt;code&gt;Counter()&lt;/code&gt; template. This code is a direct pull from &lt;a href=&quot;https://x.com/youyuxi/status/1298674615468863490?s=20&quot;&gt;a snippet Evan You shared back in 2020&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;somewhere-else.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;h2&amp;gt;Counter 1&amp;lt;/h2&amp;gt;
&amp;lt;div class=&quot;counter-1&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;h2&amp;gt;Counter 2&amp;lt;/h2&amp;gt;
&amp;lt;div class=&quot;counter-2&quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script type=&quot;module&quot;&amp;gt;

import { useCounter } from &apos;./useCounter.js&apos;;
import { effect } from &quot;@vue/reactivity&quot;;
import { html, render } from &quot;lit-html&quot;;

const Counter = () =&amp;gt; {
  const {
    increment, 
    globalCount, 
    localCount, 
    countIsEven, 
    localCountIsEven 
  } = useCounter();
  
  return () =&amp;gt; html`
    &amp;lt;div class=&quot;counter&quot;&amp;gt;
      &amp;lt;p&amp;gt;Global Count: ${globalCount.value} - isEven? ${countIsEven.value}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Local Count: ${localCount.value} - isEven? ${localCountIsEven.value}&amp;lt;/p&amp;gt;
      &amp;lt;button @click=${increment}&amp;gt;++&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  `;
}

//This marries our component instance with the DOM
//and sets up re-rendering based on state using effect
function mount( comp, target ) {
  const template = comp();
  effect( () =&amp;gt; render( template(), target) );
}

mount( Counter, document.querySelector(&apos;.counter-1&apos;) );
mount( Counter, document.querySelector(&apos;.counter-2&apos;) );

&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perhaps not the most practical example here, but I hope it&apos;s clear that this pattern gives you more options as to how you can scope reactive state to both the global and local instance.&lt;/p&gt;
&lt;p&gt;We also switch from piling everything into a &lt;code&gt;reactive&lt;/code&gt; object and instead reach for &lt;code&gt;ref&lt;/code&gt; which is similar but meant for creating reactive primitives. Separating our state and actions into individual variables allows them to be exported and then imported separately which gives you granular control over which consumers of the state can access and do what. It&apos;s also worth mentioning that with &lt;code&gt;ref&lt;/code&gt; types, we must use &lt;code&gt;&amp;lt;ref&amp;gt;.value&lt;/code&gt; to access the data, whereas, &lt;code&gt;reactive&lt;/code&gt; types, the keys can be directly accessed.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/walpolea/pen/gOERMYO?editors=1010&quot;&gt;View as Codepen Demo&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Multi-store app state&lt;/h3&gt;
&lt;p&gt;With greater app complexity comes a greater responsibility to stay organized and limit where and when state can be mutated. This last pattern continues to add a few features that help you maintain sanity across a larger application structure with multiple sets of data.&lt;/p&gt;
&lt;p&gt;Mainly I&apos;ll add two concepts that help separate mutation from state and also show that you don&apos;t need one store to rule them all; we can break things up by service or domain to allow components to specialize in their state scope.&lt;/p&gt;
&lt;p&gt;First, let&apos;s create two separate stores similar to our previous patterns:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/store/UserStore.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { reactive, computed } from &quot;@vue/reactivity&quot;;

// State
const user = reactive({
  isLoggedIn: false,
  username: null,
  profileUrl: computed( () =&amp;gt; {
    return user.username ? `/profile/@${user.username}` : undefined;
  })
});

// Actions
const User = {
  async Login( credentials ) {
    user.username = await Auth( credentials );
    user.isLoggedIn = true;
  },
  Logout() {
    user.username = null;
    user.isLoggedIn = false;
  }
}

export { user, User };

// Private functions
function Auth( credentials ) {
  // TODO: Authentication
  return &quot;UserName&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This simple User-scoped store begins by only keeping state within the reactive &lt;code&gt;user&lt;/code&gt; variable. All mutation actions are then placed in a &lt;code&gt;User&lt;/code&gt; object. I like the simple distinction of lower and uppercase to distinguish these, but feel free to name them something like &lt;code&gt;userState&lt;/code&gt; and &lt;code&gt;UserActions&lt;/code&gt; if you want to be more explicit.&lt;/p&gt;
&lt;p&gt;In this case, since the &lt;code&gt;UserStore&lt;/code&gt; is more of a singleton pattern with only global properties, we aren&apos;t using the previous pattern of wrapping our export in a function like &lt;code&gt;useUser()&lt;/code&gt; but again this is easily done if you need that instanced local state as well.&lt;/p&gt;
&lt;p&gt;Here&apos;s another store we might have in our app to track global UI state:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/store/UIStore.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import { reactive } from &quot;@vue/reactivity&quot;;

// Constants
const THEMES = [&quot;light&quot;, &quot;dark&quot;, &quot;waverunner64&quot;];

// State
const ui = reactive({
  showSettingsPanel: false,
  colorTheme: &quot;light&quot;,
});


// Actions
const UI = {
  toggleSettingsPanel(v) {
    ui.showSettingsPanel =
      v === undefined ? !ui.showSettingsPanel : v === false ? false : true;
  },
  settingsPanelOff() {
    UI.toggleSettingsPanel(false);
  },
  settingsPanelOn() {
    UI.toggleSettingsPanel(true);
  },
  changeTheme( theme ) {
    if( isValidTheme(theme) ) {
      ui.colorTheme = theme;
    }
  }
};

export { ui, UI, THEMES };

// Private functions
function isValidTheme( theme ) {
  return THEMES.includes(theme);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;ve introduced some constants that you may want to expose as helpers, but otherwise same deal here: separate state and logic into respective &lt;code&gt;ui&lt;/code&gt; and &lt;code&gt;UI&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;Now for the final piece to the pattern. While you could expose each of these state objects directly to the components that need them, I like to hide them behind one more layer that composes all of your state objects together. This creates a unified way components access state, creates a layer to inject middleware and transforms, and may make refactoring easier down the road.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;UseAppState.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import { ref, readonly } from &quot;@vue/reactivity&quot;;
import { user, User } from &quot;./userState&quot;;
import { ui, UI } from &quot;./uiState&quot;;

const initialized = ref(false);

function initializeAppState(config) {
  //We can do things like set up a database connection
  //Or load information from localStorage here
  console.log(&apos;initialize app state&apos;);
}

export const useAppState = (config) =&amp;gt; {
  if (!initialized.value) {
    initializeAppState(config);
    initialized.value = true;
  }

  return {
    ui: readonly(ui),
    user: readonly(user),
    UI,
    User,
  };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The layer here is pretty light, but a few things to point out. We&apos;ve moved back to the &lt;code&gt;useAppState()&lt;/code&gt; function to allow you to do any sort of local initialization you may need. We also have a global initialization function that will run once the first time any component or service taps into our state model. This is great for initializing database connections or loading in preset. Lastly and most importantly, we&apos;ve introduced the &lt;code&gt;readonly()&lt;/code&gt; function supplied with &lt;code&gt;@vue/reactivity&lt;/code&gt; which turns your reactive state object into a copy that cannot be directly mutated. This further safeguards our data from accidentally being changed outside of the specified action functions.&lt;/p&gt;
&lt;p&gt;To see how this can be used check out this full &lt;a href=&quot;https://stackblitz.com/edit/vue-reactivity-app-state?file=src%2Fstore%2FUIStore.js,src%2Fstore%2FUserStore.js,src%2Fstore%2FUseAppState.js,src%2Fpages%2Findex.astro&quot;&gt;Astro-based demo on stackblitz&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;tl;dr&lt;/h2&gt;
&lt;p&gt;I showed you three patterns for creating simple to more complex reactive stores that can be shared across instances of just about any sort of client-side JavaScript, be it Vue components, Astro components, React, web components or just plain &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;p&gt;One of the biggest points I want to leave you with is that these patterns aren&apos;t perfect and the pattern you choose could be a middle-ground between these, or even something much more battle-hardened than the final example. &lt;strong&gt;There isn&apos;t any exact way to build these&lt;/strong&gt;; there&apos;s an ambiguity to deal with in how flexible the library and its individual constructs are. So most of all, take into consideration your application size and needs to create a shared state pattern that works best for you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/vuejs/core/tree/main/packages/reactivity&quot;&gt;&lt;code&gt;@vue/reactivity&lt;/code&gt;&lt;/a&gt; is an underrated library that is both lightweight and mature. So I can&apos;t recommend enough taking a look if you&apos;re looking to add reactivity or shared state and data store solutions to your next project.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>What&apos;s Your Go-To Web Stack? (2024 Edition)</title><link>https://andrewwalpole.com/blog/whats-your-go-to-web-stack-2024-edition/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/whats-your-go-to-web-stack-2024-edition/</guid><description>If you had to build a website right now, get started right away, what web technologies are you reaching for in 2024?</description><pubDate>Tue, 05 Mar 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;What&apos;s Your Go-To Web Stack? (2024 Edition)&lt;/h1&gt;
&lt;p&gt;In 2022 I wrote the first, &lt;a href=&quot;/blog/whats-your-go-to-web-stack&quot;&gt;What&apos;s Your Go-To Web Stack?&lt;/a&gt; post. Now I&apos;m back a few years later to reflect on any changes to my own answer and to challenge you once again to answer the question as well. Minimally it&apos;s an amusing exercise, but I think done over time it&apos;s an interesting introspection take a quick snapshot and compare it to those of the past.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What&apos;s your go-to web stack, right now?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Here&apos;s the set-up:&lt;/h3&gt;
&lt;p&gt;I need you to build me a website right away (of course)! Content on the site will have to be updated at least once a week, but for the most part, the structure won&apos;t change much or at all. I&apos;d love to post a few short, simple blog posts every now and then and I have a contact form I&apos;d like people to reach me through. It&apos;s all designed up, sorry, a templated theme won&apos;t do.&lt;/p&gt;
&lt;p&gt;So what do you do? How do you build this site? What technologies will comprise your stack and workflow? Maybe most importantly, if you aren&apos;t quite sure, what else do you need to know?&lt;/p&gt;
&lt;h3&gt;My Go-To Web Stack&lt;/h3&gt;
&lt;p&gt;At this very moment, it&apos;s a really easy answer for me. &lt;a href=&quot;https://astro.build&quot;&gt;&lt;strong&gt;Astro&lt;/strong&gt;&lt;/a&gt; has won me over in the last few years. It&apos;s a joy to use, both in bringing me back personally to the simple focus of building websites (and not building projects that turn into websites), and also in seeing the successful role it can play in building custom production sites for clients competitively. It keeps you close to the front end, while providing the luxuries of any modern build-step framework.&lt;/p&gt;
&lt;p&gt;The static-first nature provides excellent performance, but SSR being just a toggle away gives me the flexibility and confidence to build any sort of site or even app that might get thrown at me.&lt;/p&gt;
&lt;p&gt;On top of Astro, its ability to nearly seamlessly integrate interactivity via &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue&lt;/a&gt; yields clean and performant &lt;em&gt;interactive and functional&lt;/em&gt; sites. I&apos;ve also become a steady user of &lt;a href=&quot;https://vueuse.org&quot;&gt;&lt;code&gt;vueuse&lt;/code&gt;&lt;/a&gt; and its large library of functionality-I-always-reinvent-but-dont-need-to-now-because-of-vueuse.&lt;/p&gt;
&lt;p&gt;In the CSS realm, while nesting has now arrived, I just can&apos;t quite bring myself to quit some of the niceties, or maybe just having the possibility of niceties, of &lt;code&gt;SCSS&lt;/code&gt; yet. Though I&apos;ve fully forsaken SCSS variables in favor of custom properties. I&apos;ll also give a shoutout to &lt;a href=&quot;https://ryanmulligan.dev/blog/layout-breakouts/&quot;&gt;Ryan Mulligan&apos;s Layout Breakouts&lt;/a&gt; concepts which has been doing good work for my work lately.&lt;/p&gt;
&lt;p&gt;For the dynamic content component for this project, it&apos;s tough to argue from the built-in content collections feature of Astro; if I were building the site for myself it&apos;s an easy choice. But let&apos;s say we need some sort of simpler online UI to publish content from. I would probably reach for &lt;a href=&quot;https://www.contentful.com/&quot;&gt;Contentful&lt;/a&gt; or &lt;a href=&quot;https://prismic.io/&quot;&gt;Prismic&lt;/a&gt;. Both systems are robust and capable, and each offers a more than decent free tier. This used to not be the case for Contentful, but I&apos;m now quite happy that they&apos;ve remodeled their offering to something that a lot of folks can jump into for small projects. Coming in third actually might be &lt;a href=&quot;https://airtable.com&quot;&gt;AirTable&lt;/a&gt; which I&apos;ve picked up over the last few years for some projects and it&apos;s quite good for small projects.&lt;/p&gt;
&lt;p&gt;For the form, I think my serverless game has grown to the level that I would likely just roll it myself over attempting to pay, integrate and style a 3rd-party service. Let&apos;s just parse out that data and send it on over to our CMS or maybe even right to email.&lt;/p&gt;
&lt;p&gt;As for hosting, I think my conviction for one host over another has waned a bit. I am still mostly positive about the offerings and capabilities of &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;Cloudflare&lt;/a&gt; and &lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt; and would likely choose one of those, but I&apos;m less stoked about their in-flux reputations. For sure Netlify has just gone and fallen off my radar, are they even still around? Maybe a new old player is AWS Amplify, which just launched SSR support after being static-only for so many years; but it&apos;s a bit early to tell how that will play out. Lastly, while I haven&apos;t done much with them, I keep a longing gaze turned toward &lt;a href=&quot;https://deno.com/deploy&quot;&gt;Deno Deploy&lt;/a&gt;. All the things Deno is doing are cool and good, and I want to support that.&lt;/p&gt;
&lt;h3&gt;tl;dr&lt;/h3&gt;
&lt;p&gt;Once again this exercise was a lot of fun to reflect on. Many of my answers changed. A few years ago I was slinging &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; and &lt;a href=&quot;https://nuxt.com/&quot;&gt;Nuxt&lt;/a&gt; sites like nobody&apos;s business, and while I am still fond of what they can do, the layered-on happy-defaults simplicity of Astro quickly won me over. In that time I&apos;ve also switched jobs, but I think it&apos;s interesting how that transition only supported the continued trajectory I was on with the web and did not cause any major disruption to it.&lt;/p&gt;
&lt;p&gt;For me, at this moment, my go-to web stack is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;li&gt;Vue + vueuse&lt;/li&gt;
&lt;li&gt;SCSS&lt;/li&gt;
&lt;li&gt;Contentful / Custom Serverless Forms&lt;/li&gt;
&lt;li&gt;Vercel&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Invisible Contexts</title><link>https://andrewwalpole.com/blog/invisible-contexts/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/invisible-contexts/</guid><description>My thoughts on the devaluing of front-end development, and how dealing with invisible contexts has become thankless work.</description><pubDate>Sat, 23 Mar 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Invisible Contexts&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://joshcollinsworth.com/blog/devaluing-frontend&quot;&gt;Josh Collinsworth recently put out an incredibly well-written article&lt;/a&gt; about his perceptions of the Front-end Web Development role being devalued within the industry. If you haven&apos;t given it a read, please do!&lt;/p&gt;
&lt;p&gt;Josh sums up his article saying he just &lt;em&gt;feels it mostly&lt;/em&gt;. Well I feel it too and I&apos;ve been thinking a lot about why that is.&lt;/p&gt;
&lt;p&gt;For me, there are two clear places I see front-end development, and especially front-of-the-front-end (HTML, CSS, DOM-facing JavaScript), being devalued.&lt;/p&gt;
&lt;h2&gt;The Rise of the Full-stack Developer&lt;/h2&gt;
&lt;p&gt;The last decade of web development has seen multiple balancings and rebalancings of the importance and need for both front and back-end developers. And so as the latest and greatest client and server technologies waged land wars, we sold away that frustration and uncertainty with the idea of a role that can just do it all and break the hard divide between the two.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Full-stack Developer was borneth!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It looks great on paper, especially to the payroll department: One person to fill traditionally two roles. But in reality, we know it doesn&apos;t work that way. It may be a role for a technology generalist to thrive in, but one person&apos;s effort is finite, and consistent, quality development across the entire product development spectrum requires focus and expertise. Nevertheless, start-ups soaked up the efficiency, and in a tumultuous churn of web tech it was a decent defense.&lt;/p&gt;
&lt;p&gt;Fast-forward a few years, the full-stack idea has remained, but those raging wars have mostly subsided. With React and Node&apos;s grip on the industry, and then a rise in serverless architecture, it seems like both sides have reached an agreement: &lt;strong&gt;JavaScript&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;History lesson aside, how does this devalue front-end development? Whether it&apos;s mostly truth or just stereotype, the idea has been set: Full-stack devs got by with shirking front-of-the-front-end tech and they still built the apps. Ergo, The front end can&apos;t be &lt;em&gt;that&lt;/em&gt; important. It&apos;s the &lt;em&gt;real engineering&lt;/em&gt; that matters, right?&lt;/p&gt;
&lt;p&gt;I might even venture to surmise that concepts like CSS-in-JS, Tailwind and others were born out of necessity from these types of roles looking to squeeze down the uncompromising breadth of technologies they&apos;ve had to contend with. Once again, putting a message out there that we don&apos;t need the raw, deep capabilities of CSS.&lt;/p&gt;
&lt;p&gt;In the same sense that I sure can build a website in plain HTML, they sure can build an App with just Javascript and a Database. This was how software was back in the &apos;90s and early &apos;00s. UX was icing on the cake, not yet a competitive business strategy. So sure, you &lt;em&gt;can&lt;/em&gt; do that, but it doesn&apos;t prove the argument. UX and design are tablestakes now, and the industry isn&apos;t entertaining a step backward.&lt;/p&gt;
&lt;h2&gt;The Rise of UX Design&lt;/h2&gt;
&lt;p&gt;That last paragraph is a perfect segue into the other big reason I think front-end development is being devalued: UX Design has been and continues to be, &lt;em&gt;in the spotlight&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What&apos;s not to love about it? A beautiful, thoughtful set of pretty pictures and content. I see what looks like a button and my built-up knowledge of foundational UX patterns as a digitally savvy user has me say,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Aha! I can click that! Bravo, designer!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;An accordion? Yes! I know what to do with that too. How lovely!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;What&apos;s this boring bit with the words and photo? Oh, it does parallax? That sounds fancy, I can&apos;t envision it exactly, but sure, sounds amazing!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Don&apos;t get me wrong, I think UX Design is incredibly important. I &lt;em&gt;love&lt;/em&gt; that it now is as important as it is. I even switched career paths from Software Engineering to Web Development because I wrote a technical paper on the importance of considering the user experience of functional components when building OS-level software, amidst a dull curriculum of C++ and low-level Windows programming.&lt;/p&gt;
&lt;p&gt;So don&apos;t take this as me being mad at UX Designers, I&apos;m not. If anything, they&apos;ve done such a good job in their roles, the hand-off to the next step, front-end development, implementation, feels like someone just shut down the party.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;It&apos;s going to take how long? And there will be bugs at first? And when you&apos;re done it will look the same as it looks in figma, just in a browser? The designer already designed it, isn&apos;t the hard work done?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some of it is, yes, but we now need to deal with (and we&apos;re finally at the title of the blog post):&lt;/p&gt;
&lt;h2&gt;Invisible Contexts&lt;/h2&gt;
&lt;p&gt;Let&apos;s take charge of the problem: Listen fellow front-enders, we have a marketing problem. To our left and our right, the roles around us are dazzling the project managers, clients and VPs with their voodoo and techno-babble. We have voodoo too, but no one sees it. Our role is filled with detail; minutia. We take beautiful broad-stroked ideas and birth them into ugly reality.&lt;/p&gt;
&lt;p&gt;Doing that for the web means taking one or two, or if we&apos;re lucky, three different screen designs and translating them into a dynamic functional system that works on all screens, all browsers, all operating systems, all devices, all users, all functional states, all user journeys and stories, etc. These are the &lt;em&gt;incredibly complex, compounded situations of reality&lt;/em&gt; that we must work through, these are the &lt;strong&gt;invisible contexts&lt;/strong&gt; that make up the primary function of the job.&lt;/p&gt;
&lt;p&gt;And yet they do remain mostly invisible. I don&apos;t see this work being valued in the same way it is for the UX designer; the credit was already given for the broad idea, this next step is nothing new.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;When do we get to launch? That will be exciting!&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&apos;t necessarily have an answer to propose to conclude this post. But I did want to end with a fun graphic and connect one more idea. &lt;a href=&quot;https://frontendmasters.com/blog/design-engineers/&quot;&gt;Chris Coyier pointed out that &lt;em&gt;Design Engineering&lt;/em&gt; is having a moment&lt;/a&gt; and maybe this is our Fresh Start™ opportunity.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;I like to think that the role of Design Engineer brings these invisible contexts into clearer focus. It&apos;s a role that shifts left from Front-end Developer, encompassing some UX design responsibilities around synthesizing functionality amidst design, and centers on that transition from rough idea to exact reality: What &lt;em&gt;actually is the experience you build&lt;/em&gt;? &lt;strong&gt;I like it&lt;/strong&gt;. I think this is a shift that potentially unlocks new value, value that may solve our devaluing problem.&lt;/p&gt;
&lt;p&gt;And we&apos;ve seen it before. The Apple of yesterday was filled with roles that centered on that &lt;em&gt;real experience&lt;/em&gt;, and we saw the rewards that came from that, they were next level; like nothing else. And now we see how it&apos;s so easily lost when that focus is lost, and they give in to the devaluing.&lt;/p&gt;
&lt;p&gt;So I for one am excited to see this solidify further, perhaps it&apos;s still grass-roots at the moment, but the roles do exist sparsely here and there. The only question left is if we do fully shift left, what becomes of the traditional Front-end Developer? Perhaps it&apos;s the place Full-stack belonged all along.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>The Concentrated Water Cooler</title><link>https://andrewwalpole.com/blog/the-concentrated-water-cooler/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/the-concentrated-water-cooler/</guid><description>The era of remote work requires change in the ways spatially established habits have been disrupted, including socializing and culture building.</description><pubDate>Thu, 04 Apr 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;The Concentrated Water Cooler&lt;/h1&gt;
&lt;p&gt;I wrote previously about how &lt;a href=&quot;/blog/remote-leadership-takes-a-concerted-effort&quot;&gt;remote leadership takes a concerted effort&lt;/a&gt;. For companies that have gone and stayed remote due to the pandemic, the work environment has been severely altered. What maybe wasn&apos;t so clear is that this means &lt;em&gt;all of the behaviors&lt;/em&gt; that make up what work looks like also need reexamination. That includes how co-workers socialize and fill in the gaps of what the company does with the culture of who the company is comprised of.&lt;/p&gt;
&lt;p&gt;Water cooler talk is the old slang that stuck around, denoting the place where co-workers might find themselves engaging in conversations beyond the boundaries of work-at-hand. These encounters are casual and provide an opportunity for personalities to come out and tighter relationships to form, one water refill at a time.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Did you catch the game last night?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&apos;t know who talks like that, but it seems to be the right type of thing to use as an example here.&lt;/p&gt;
&lt;p&gt;In the age of remote work, there are a lot of culture-building moments like that lost behind the screen divide. So, in similar fashion to remote leadership, building strong remote-based company cultures takes a concerted effort.&lt;/p&gt;
&lt;p&gt;Should you schedule a 10-minute zoom or slack huddle every morning? I don&apos;t think so. In most cases, trying to emulate the same concepts that work in-person can be an even bigger issue than not doing them at all.&lt;/p&gt;
&lt;p&gt;And on top of that, we should also reconsider the affordances of remote work. Like what was previously commute and water cooler time, may now be time spent with the kids before school, or conversations with your partner or friends, delivering value back to &lt;em&gt;you&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;While value back to you does help the company, because &lt;em&gt;you&lt;/em&gt; and your well-being matter. This doesn&apos;t alleviate the need to establish some sort of social culture between people at work; &lt;strong&gt;relationships matter, especially at work&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So I propose &lt;strong&gt;the concentrated water cooler&lt;/strong&gt;: The idea that each of those small but now missed daily social interactions a company would accumulate each day across many employees should be viewed as accruing a debt of social need. And thus, as that debt accumulates to a non-trivial amount, it should be paid off in a larger lump sum: &lt;em&gt;Get people together&lt;/em&gt;! Once or twice a year, fly them out, plan some events, make it simple, light and fun, &lt;em&gt;invest in your social and cultural capital&lt;/em&gt;. Cancel work for two or three days and let people do what they do.&lt;/p&gt;
&lt;p&gt;Now I&apos;m going to be particular about this: If you have a big company, &lt;em&gt;split it up&lt;/em&gt;. Two&apos;s company, three&apos;s a crowd, five to thirty is a party and any more than that is a panic attack waiting to happen. Get a few teams or a department together. The folks that already see each other day-in and day-out on zoom. That&apos;s it. &lt;em&gt;The goal is relationship building&lt;/em&gt;, and the good news is usually it just takes a neat atmosphere, some food and drink, and people will do what they do. Skip the games and agenda, or at least keep it real loose.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>The Showy / Hidey Navigation Bar</title><link>https://andrewwalpole.com/blog/the-showy-hidey-nav-bar/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/the-showy-hidey-nav-bar/</guid><description>An in-demand pattern distilled – a navbar that hides as you scroll down a page, but then instantly reappears and follows you when you scroll up.</description><pubDate>Mon, 08 Apr 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;The Showy / Hidey Navigation Bar&lt;/h1&gt;
&lt;p&gt;That&apos;s definitely the technical term for it. It&apos;s that popular navbar pattern where the header disappears above the screen as you scroll down, affording you more content consumption space, but then, as if it were waiting just out of view for you, reappears upon the slightest scroll up.&lt;/p&gt;
&lt;p&gt;I&apos;ve built so many of these over the last few years, I decided to distill the pattern down. Essentially we need just a few ingredients to pull it off:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A container separate from the &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; to sticky down the page.&lt;/li&gt;
&lt;li&gt;Some &lt;code&gt;javascript&lt;/code&gt; to let us know when we&apos;ve scrolled up or down.&lt;/li&gt;
&lt;li&gt;And if you don&apos;t want things to get too janky, an understanding of a minimum height for our header to be.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For our &lt;code&gt;html&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;header&amp;gt;
  &amp;lt;div class=&quot;navbar&quot;&amp;gt;
    &amp;lt;!-- All the nav stuff--&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/header&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Focusing just on the pattern, I&apos;ve left the innards of the navbar up to you, maybe a logo and a list of links would be good. We really just need the extra &lt;code&gt;.navbar&lt;/code&gt; class to &lt;code&gt;position: fixed;&lt;/code&gt; while the &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; stays &lt;code&gt;position: static;&lt;/code&gt; to hold the space for the navbar. You can see that in our &lt;code&gt;CSS&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;header {
  --nav-height: 100px;
  min-height: var(--nav-height);
  
  .navbar {
    --top-position: 0px;
    position:fixed;
    width:100%;
    top: var(--top-position);
    
    transition: top 0.6s;
    
    &amp;amp;.hide:not(:focus-within) {
      --top-position: -100%;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As I mentioned, setting the &lt;code&gt;--nav-height&lt;/code&gt; is important because without letting the component know how tall it might be, it can&apos;t hold any space at the top of the page for the navbar to sit into. So either your first content section will run under the navbar, or if you toggle it between &lt;code&gt;static&lt;/code&gt; and &lt;code&gt;fixed&lt;/code&gt; positioning when you scroll, it will jump the screen up as it&apos;s taken out of the flow. It&apos;s all much simpler if you just hold the right amount of pixel space.&lt;/p&gt;
&lt;p&gt;&amp;lt;aside&amp;gt;&lt;/p&gt;
&lt;p&gt;As an aside, you might think I&apos;m overdoing it here and you should just use &lt;code&gt;position: sticky;&lt;/code&gt;. And indeed, this will keep the header in the flow and then sticky it to the top as you scroll. But I&apos;ve gone down this path a few times and I continue to hit major barriers with sticky and the togglable mobile menu you may eventually build into the nav. So until I can get past that barrier I will continue to recommend &lt;code&gt;position:fixed;&lt;/code&gt; for this pattern.&lt;/p&gt;
&lt;p&gt;&amp;lt;/aside&amp;gt;&lt;/p&gt;
&lt;p&gt;Next, we default the &lt;code&gt;top&lt;/code&gt; to &lt;code&gt;0px&lt;/code&gt; and change it to &lt;code&gt;-100%&lt;/code&gt; when it has the &lt;code&gt;.hide&lt;/code&gt; class applied. Add in a transition and your navbar is ready to hide and show elegantly.&lt;/p&gt;
&lt;p&gt;It&apos;s also worth mentioning the &lt;code&gt;:not(:focus-within)&lt;/code&gt; bit, which allows the navbar to not be hidden when we give focus to the nav items. So as you tab through with a keyboard, the navbar will reappear.&lt;/p&gt;
&lt;p&gt;Finally, we need some &lt;code&gt;javascript&lt;/code&gt; to hook it all together. Though when scroll-driven animations are widely available, perhaps we won&apos;t need that at all.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const useShowyHidey = () =&amp;gt; {
  let lastScrollTop = 0;
  const navbar = document.querySelector(&quot;.navbar&quot;);
  
  const toggleNavOnScroll = () =&amp;gt; {
    const st = document.documentElement.scrollTop;
    if ( st &amp;gt; lastScrollTop &amp;amp;&amp;amp; st &amp;gt; parseInt(getComputedStyle(navbar).getPropertyValue(&quot;--nav-height&quot;)) ) {
      navbar.classList.add(&quot;hide&quot;);
    } else if (st &amp;lt; lastScrollTop) {
      navbar.classList.remove(&quot;hide&quot;);
    }
    lastScrollTop = st &amp;lt;= 0 ? 0 : st;
  }
  
  if (navbar) {
    window.removeEventListener( &quot;scroll&quot;, toggleNavOnScroll);
    window.addEventListener( &quot;scroll&quot;, toggleNavOnScroll);
  }
}

useShowyHidey();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We listen to the scroll event and toggle the &lt;code&gt;.hide&lt;/code&gt; class based on scroll direction.&lt;/p&gt;
&lt;p&gt;And here&apos;s &lt;a href=&quot;https://codepen.io/walpolea/pen/eYoMOLp&quot;&gt;the whole thing in a codepen&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;eYoMOLp&quot; data-user=&quot;walpolea&quot; style=&quot;height: 500px; 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/walpolea/pen/eYoMOLp&quot;&amp;gt;
Showy / Hidey Navigation Bar&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Fast and Simple APIs with Deno KV</title><link>https://andrewwalpole.com/blog/fast-and-simple-apis-with-deno-kv/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/fast-and-simple-apis-with-deno-kv/</guid><description>Look out! There&apos;s a new persistent edge KV store in town! Let&apos;s take Deno KV and Deno Deploy for a spin by building a simple API.</description><pubDate>Tue, 12 Sep 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Fast and Simple APIs with Deno KV&lt;/h1&gt;
&lt;p&gt;In a previous post, I dove into how you can &lt;a href=&quot;https://www.andrewwalpole.com/blog/building-a-like-button-with-cloudflare-workers/&quot;&gt;build a like button with Cloudflare Workers and KV&lt;/a&gt;. Well, there&apos;s finally a new rival service in town: &lt;em&gt;Deno KV.&lt;/em&gt; Let&apos;s take a look!&lt;/p&gt;
&lt;p&gt;In the process of building that same likes service in Deno KV, I have to say I&apos;m quite impressed with how quick and easy it was to go from code to production. I&apos;ll outline the steps it took to get things going and then walk through the details.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sign up for a free &lt;a href=&quot;https://deno.com/deploy&quot;&gt;Deno Deploy&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;Create a new project and choose the &lt;code&gt;&quot;Hello World&quot;&lt;/code&gt; template with the &lt;code&gt;Try with Playground&lt;/code&gt; button. Similar to Cloudflare&apos;s online editor, you get plopped right into an online coding environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;img src=&quot;/static/blog/deno-playground.png&quot; alt=&quot;Screenshot of the Deno Playground with code&quot;&amp;gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From there you can code, test and deploy your API until you have it just right. And then optionally you can move that code over to GithHb when you&apos;re ready to graduate from the playground experience.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&apos;s also worth noting that you can do all of this from your local machine via &lt;code&gt;deployctl&lt;/code&gt; which is Deno Deploy&apos;s CLI tool. You just need to create a project and generate an API key in your account to deploy with a command like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;deployctl deploy --token=&amp;lt;YOUR API KEY&amp;gt; --project=&amp;lt;YOUR PROJECT NAME&amp;gt; main.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;A neat benefit of doing it this way is that you can locally run your code with &lt;code&gt;deno run --allow-net=:8000 --unstable main.ts&lt;/code&gt; and even Deno KV will work for full local end-to-end testing.&lt;/p&gt;
&lt;h2&gt;Writing the API Code&lt;/h2&gt;
&lt;p&gt;Whether you&apos;re working locally or on the Deno playground, setting up our likes API is pretty straightforward.&lt;/p&gt;
&lt;p&gt;At the heart of it all, your Deno Deploy code is running a Deno script. So if you want to do web stuff, you&apos;ll want to set up a server. That server will have a handler function where you can encounter any request on your project and make it all do something.&lt;/p&gt;
&lt;p&gt;And for good measure, check out the &lt;code&gt;respondJSON()&lt;/code&gt; helper function I have included which will enable CORS on your endpoint and respond with JSON data. This is very important if you want some other website to be able to use your API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { serve } from &quot;https://deno.land/std@0.140.0/http/server.ts&quot;;
const kv = await Deno.openKv(); //Initialize Deno KV

async function handler(_req: Request) {
  //Do servery stuff here
}

//start the server
serve(handler);

//A helper to return CORS enabled JSON data
function respondJSON( data, status = 200 ) {
  
  const corsHeaders = {
    &apos;Access-Control-Allow-Origin&apos;: &apos;*&apos;,
    &apos;Access-Control-Allow-Headers&apos;: &apos;*&apos;,
    &apos;Access-Control-Allow-Methods&apos;: &apos;GET,HEAD,POST,OPTIONS&apos;,
  };

  return new Response(JSON.stringify(data, null, 2),{
    status,
    headers: {
      ...corsHeaders,
      &apos;content-type&apos;: &apos;application/json;charset=UTF-8&apos;,
    },
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&apos;re building a little API, so the servery stuff we want to do is parse out the request information and see if it matches up with a command like retrieving or posting likes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async function handler(_req: Request) {

  //Browsers love to pre-hit endpoints with an &quot;OPTIONS&quot; method
  //this will ensure we&apos;re handling that with CORS
  if( _req.method === &quot;OPTIONS&quot; ) {
    return respondJSON({ok: &quot;OK&quot;});
  }
  
  //the _req object has lots of goodies to use
  //We parse the url sent in to access the parthname and params
  const { method } = _req;
  const url = new URL(_req.url);
  const id = url.searchParams.get(&apos;id&apos;);
  
  //Define some routes and functions to call when they&apos;re hit:
  const routes = {
    &apos;/likes&apos;: {
      GET: async (id) =&amp;gt; await getLikes(id),
      POST: async (id) =&amp;gt; await postLike(id)
    },
    &apos;/likes/reset&apos;: {
      GET: async (id) =&amp;gt; await resetLikes(id),
    }
  };
  
  //Now, if we match a route and have an id
  //we can call the appropriate route function
  if( routes[url.pathname] &amp;amp;&amp;amp; id ) {
    const responseData = await routes[url.pathname]?.[method](id);
    return respondJSON( responseData );
  }

  //oops, if something went wrong return an error
  return respondJSON({ success: false }, 404);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ok, that&apos;s the API setup: it has some route logic and knows what functions to call. Let&apos;s now set up those functions and finally get to using Deno KV!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//Returns the number of likes an Id has
async function getLikes(id) {

  //Query the value based on the Id
  const res = await kv.get([&apos;likes&apos;, id]);

  //If the Id doesn&apos;t exist in KV set it to 0
  if( res.value === null ) {
    await kv.set([&apos;likes&apos;, id], 0);
  }

  return { success:true, id, likes: res.value ?? 0 };
}

//Adds a like to an Id
async function postLike(id) {

  //Query the current likes and then set it to current+1
  const { likes } = await getLikes(id);
  const res = await kv.set([&apos;likes&apos;, id], likes+1 );
  
  //oops the operation failed
  if( !res.ok ) {
    return { success: false, likes }
  }

  return { success: true, id, likes: likes+1 };
}

//Resets the Id&apos;s likes to 0.
//TODO: Add a secret password parameter check here
async function resetLikes(id) {

  const res = await kv.set([&apos;likes&apos;, id], 0 );
  
  if( !res.ok ) {
    return { success: false }
  }

  return { success: true, id, likes: 0 };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that&apos;s it! You can see the &lt;a href=&quot;https://github.com/walpolea/Deno-KV-Likes&quot;&gt;production code on github&lt;/a&gt;, and see the API in action below:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;500&quot; data-default-tab=&quot;js,result&quot; data-slug-hash=&quot;dywWNzv&quot; data-user=&quot;walpolea&quot; style=&quot;height: 500px; 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/walpolea/pen/dywWNzv&quot;&amp;gt;
Likes Counter with Deno KV&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;Overall, I&apos;m really happy with Deno KV. It&apos;s simple to use, and paired with the Deno Deploy platform it&apos;s quick to get up and running with. If I have to critique something, I do think the documentation could use some zhuzhing, as I did get turned in circles for a few days before figuring things out properly.&lt;/p&gt;
&lt;p&gt;I look forward to playing more with Deno KV and the Deploy service. Especially for you front-end folks, this is an incredibly powerful back-end solution to lean on when you need that little bit of persistent or shared state for the thing you&apos;re building.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Takeaways from a Weekend Project</title><link>https://andrewwalpole.com/blog/takeaways-from-a-weekend-project/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/takeaways-from-a-weekend-project/</guid><description>A few musings and insights from a short bit of work conducted over the weekend.</description><pubDate>Wed, 17 Apr 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Takeaways from a Weekend Project&lt;/h1&gt;
&lt;p&gt;This weekend I built a simple web app to configure and generate a layout breakouts CSS pattern: &lt;a href=&quot;https://layout-breakouts-builder.vercel.app/&quot;&gt;Layout Breakouts Builder&lt;/a&gt;. Over a few coding sessions, all together, it maybe took ~4 hours of work.&lt;/p&gt;
&lt;p&gt;This post isn&apos;t so much about the Layout Breakouts pattern, though I do think it&apos;s quite useful! Instead, I&apos;d like to quickly reflect on doing projects like this.&lt;/p&gt;
&lt;p&gt;So here are my takeaways:&lt;/p&gt;
&lt;h2&gt;Starting is hard and easy&lt;/h2&gt;
&lt;p&gt;The idea of starting a project like this seems hard.&lt;/p&gt;
&lt;p&gt;I&apos;ve been wanting to build this thing for months but just couldn&apos;t overcome the idea that it would be too hard to get started.&lt;/p&gt;
&lt;p&gt;The act of starting was actually quite easy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Identify first step. Do first step.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this case, I sat down and said, &quot;ok, what&apos;s one thing this thing needs to do?&quot; The answer was that it needed to output some formatted CSS code based on some variable values. So that&apos;s where I started. I built a function that took in some parameters and returned formatted text. I stuffed that text into a &lt;code&gt;&amp;lt;pre&amp;gt;&lt;/code&gt; tag, and from there I was off to the races on the whole thing.&lt;/p&gt;
&lt;h2&gt;Having a go-to web stack is useful&lt;/h2&gt;
&lt;p&gt;As a web developer, having a quick &lt;a href=&quot;/blog/whats-your-go-to-web-stack-2024-edition/&quot;&gt;go-to web stack&lt;/a&gt; to build projects like this is essential. So many times I&apos;ve had project ideas of grandeur, only to get stalled immediately in the technical architecture because I didn&apos;t have the right tools on-hand to get started.&lt;/p&gt;
&lt;p&gt;But not this time. My muscle memory for Astro + Vue hosted on github + vercel has become strong enough that these things faded away into the background, letting me focus on the core functionality.&lt;/p&gt;
&lt;p&gt;If you don&apos;t have this at-hand, I recommend going into real prototype mode and using something like &lt;a href=&quot;https://codepen.io&quot;&gt;codepen&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Vueuse useClipboard is great!&lt;/h2&gt;
&lt;p&gt;One of the daunting flourishes for this app was to add a &quot;copy code&quot; button. I&apos;ve done this before, actually quite a bit, but it always leaves me feeling drained because the easy way is touted as deprecated, and the harder way has support quirks, and when you try to land on a way forward you&apos;re 12 stack-overflow questions in just wishing for a &lt;code&gt;copy( text )&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Well &lt;a href=&quot;https://vueuse.org/core/useClipboard/&quot;&gt;&lt;code&gt;@vueuse/core useClipboard&lt;/code&gt;&lt;/a&gt; to the rescue! I&apos;ve been using the library quite a bit, and it has a ton of useful Vue composables. But given the haunting scenario I just listed above looming over me, it was an extra &lt;em&gt;chef&apos;s kiss&lt;/em&gt; moment for me.&lt;/p&gt;
&lt;p&gt;The code looked something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { useClipboard } from &apos;@vueuse/core&apos;;
const { copy } = useClipboard();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then later on my copy button:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button @click=&quot;copy( lb.generateCSS() )&quot;&amp;gt;Copy CSS&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;Constructable Stylesheets are cool!&lt;/h2&gt;
&lt;p&gt;Credit given where credit is due: This project happened thanks to the crew over at the &lt;a href=&quot;https://www.patreon.com/shoptalkshow&quot;&gt;Shop Talk Show d-d-d-discord&lt;/a&gt;. Someone talked about building a similar code-generating tool, and someone else mentioned using &lt;a href=&quot;https://web.dev/articles/constructable-stylesheets&quot;&gt;Constructable Stylesheets&lt;/a&gt; to make it easy and performant. Well that was it for me, I had to try it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Spoiler alert&lt;/strong&gt;: it &lt;em&gt;was easy&lt;/em&gt; and &lt;em&gt;is performant&lt;/em&gt;! I was able to instantiate a new &lt;code&gt;CSSStyleSheet&lt;/code&gt; and apply it to the page, and then use the &lt;code&gt;replaceSync( codeAsText )&lt;/code&gt; function to update the styles as the editor generated them. Worked like a charm!&lt;/p&gt;
&lt;p&gt;The code looked something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const sheet = new CSSStyleSheet();
document.adoptedStyleSheets = [sheet];

watch( lb, () =&amp;gt; {
  sheet.replaceSync( lb.value.generateCSS() );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;br&amp;gt;&lt;/p&gt;
&lt;h2&gt;Do this (more) often&lt;/h2&gt;
&lt;p&gt;Lastly, a reminder to myself and suggestion to you all: &lt;em&gt;Do this more often&lt;/em&gt;. Make stuff you&apos;re passionate about. The process and the result are rewarding in so many ways.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Thoughtful Communication</title><link>https://andrewwalpole.com/blog/thoughtful-communication/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/thoughtful-communication/</guid><description>As a person ingrained in technical details I&apos;ve come to value the ability to communicate with others in an effective way.</description><pubDate>Fri, 12 Apr 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Thoughtful Communication&lt;/h1&gt;
&lt;p&gt;I have built up some decent technical skills over the years, but out of all the daily things I do that make me feel effective in executing that technical work within the context of a larger team, &lt;strong&gt;communicating&lt;/strong&gt; stands out as a skill that has the power to make or break the entire pace of a project.&lt;/p&gt;
&lt;p&gt;Like most of these types of &quot;soft skills&quot; the secret to them is mostly to build awareness that you&apos;re doing them: &lt;em&gt;I&apos;m communicating now, let me consider my words; &lt;strong&gt;be thoughtful.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Especially in a world where a lot of working communication now happens via Slack, there&apos;s opportunity to put extra thought into your words.&lt;/p&gt;
&lt;p&gt;For me, effective communication is about three things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting the proper context for the person or people being communicated to.&lt;/li&gt;
&lt;li&gt;Understanding what the main points I&apos;m trying to convey are.&lt;/li&gt;
&lt;li&gt;And removing ambiguity, or said another way, including proper clarity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Context Setting&lt;/h2&gt;
&lt;p&gt;I think of setting the context as about starting the recipient of your message in a place that is known or familiar to them and then leading them into the new thing you&apos;re trying to convey. I&apos;ll usually start my messages&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Do you remember that weird bug we discussed last week on clientsite.com, I&apos;ve done some investigating and here is what I found:&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;I have reviewed the mock-up that you sent me (inserts link to mock-up), I had a few follow-up questions about it:&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Can we talk about project X? I want to make sure we are aligned on the timing and budget...&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Maybe messages that start out this way seem normal to you. That&apos;s probably a good sign. But I do regularly encounter folks launching right into the middle of a thought, and we have to spend time walking things back and uncovering the context to have the effective part of the conversation. I even catch myself doing it, but usually, upon re-reading the message before hitting send, I will realize my opportunities for setting context and make revisions.&lt;/p&gt;
&lt;p&gt;Think of it this way: You&apos;re in the mode of the thing you are communicating, but they might not be. Say a few words to prepare their brain to take in some new information about a thing that at this moment may have fully fallen out of their focus.&lt;/p&gt;
&lt;h2&gt;Get to the points&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Bullet points that is!&lt;/em&gt; I would say that if I ever Slack a message longer than one or two sentences, you can bet it likely has bullet points. It&apos;s also a reason why most of the context statements above end in a colon; because once the context is set it&apos;s time for bullets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bullets empower you to focus on the main point of the message.&lt;/li&gt;
&lt;li&gt;You can order them in a way that they build off of each other as you go,&lt;/li&gt;
&lt;li&gt;Allowing the recipients to digest the information in a way that is logical.&lt;/li&gt;
&lt;li&gt;You can use them to effectively split out information.&lt;/li&gt;
&lt;li&gt;Like explaining something that happened&lt;/li&gt;
&lt;li&gt;And then listing out the resultant consequences of it happening.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Remove Ambiguity&lt;/h2&gt;
&lt;p&gt;Ambiguity can be tough to self-assess; you can&apos;t get it right every time. It&apos;s also a careful balance between being concise and being clear. &lt;em&gt;Can what I just said or wrote be interpreted in multiple ways?&lt;/em&gt; If so, it can help to add words that clarify statements, or reframe statements entirely:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;We can start working on this project earlier than expected in order to meet the set deadline, but with the increase in project scope we should revisit the overall development budget. Or said another way, I have concerns that what we&apos;re now being asked to do will require more hours of effort.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;On &lt;code&gt;line 36&lt;/code&gt; of &lt;code&gt;CarouselGridTabs.vue&lt;/code&gt; you are referencing a property that is not always set via the component props. Can you make sure to account for that situation and think through any other situations the component might have trouble dealing with?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Yes, let&apos;s revert only today&apos;s changes that were pushed to production. To be clear: production should be reverted to reflect it&apos;s state from yesterday, and we will keep our newer updates in staging until we are given the greenlight to re-release them&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Like this last example, I think there is a lot of clarity to be given when answering questions. Try not to stick to simple &quot;yes, no, ok&quot; answers and be clear on what the answer entails. Using qualifying words like &quot;only&quot; or &quot;never&quot; or specific dates, versions or other contextual identifiers go a long way in sending clear messages.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;Putting all these things together makes for a decent framework to start communicating effectively:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask for clarity if you are having trouble figuring out how to respond&lt;/li&gt;
&lt;li&gt;On-ramp your recipient to your message with context&lt;/li&gt;
&lt;li&gt;Rre-read all long messages and revise words constantly&lt;/li&gt;
&lt;li&gt;Add qualifiers to disambiguate&lt;/li&gt;
&lt;li&gt;Cut extraneous information and stick to your main points&lt;/li&gt;
&lt;li&gt;Cite your sources&lt;/li&gt;
&lt;li&gt;And use lots and lots of lists!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But mostly it takes practice: &lt;em&gt;Be in the moment when communicating; &lt;strong&gt;be thoughtful.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Custom Top and Bottom CSS Container Masks</title><link>https://andrewwalpole.com/blog/custom-top-and-bottom-css-container-masks/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/custom-top-and-bottom-css-container-masks/</guid><description>A simple and effective way to apply a custom mask to the top and/or bottom of any container with CSS.</description><pubDate>Wed, 29 May 2024 12:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&amp;lt;style&amp;gt;&lt;/p&gt;
&lt;p&gt;.mask-container, .mask-box {
/* The image used as a mask for the top of the container &lt;em&gt;/
--top-mask-image: url(&apos;data:image/svg+xml,&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 1000 100&quot;&amp;gt;&amp;lt;path d=&quot;M0,100S32.06,0,326.72,0c255.72,0,358.35,119.2,673.28,77.47v22.53H0Z&quot;/&amp;gt;&amp;lt;/svg&amp;gt;&apos;);
--top-mask-image-height: 100; /&lt;/em&gt; The width of the top mask image &lt;em&gt;/
--top-mask-image-width: 1000; /&lt;/em&gt; The height of the top mask image */&lt;/p&gt;
&lt;p&gt;/* Calculate the height of the top mask */
--top-mask-height: calc( ( var(--top-mask-image-height) / var(--top-mask-image-width) * 100 ) * 1cqw - 2px );&lt;/p&gt;
&lt;p&gt;/* Define all the same for the bottom &lt;em&gt;/
--bottom-mask-image: url(&apos;/static/blog/css-masks/swoop-bottom.svg&apos;);
--bottom-mask-image-height: 100; /&lt;/em&gt; The width of the bottom mask image &lt;em&gt;/
--bottom-mask-image-width: 1000; /&lt;/em&gt; The height of the bottom mask image */&lt;/p&gt;
&lt;p&gt;/* Calculate the height of the bottom mask */
--bottom-mask-height: calc( ( var(--bottom-mask-image-height) / var(--bottom-mask-image-width) * 100 ) * 1cqw - 2px );
}&lt;/p&gt;
&lt;p&gt;.mask-container {
/* Define a container to use cqw instead of vw units */
container-type: inline-size;&lt;/p&gt;
&lt;p&gt;/* Added bonus: apply padding to container above the .mask-box equal to the negative margin */
&amp;amp; div:has(+ .mask-box) {
padding-block-end: var(--top-mask-height);
}&lt;/p&gt;
&lt;p&gt;/* Added bonus: apply padding to container below the .mask-box equal to the negative margin */
&amp;amp; .mask-box + div {
align-items: start;
padding-block-start: var(--bottom-mask-height);
}
}&lt;/p&gt;
&lt;p&gt;.mask-box {&lt;/p&gt;
&lt;p&gt;/* Apply negative margin to the top and bottom */
margin-block: calc( -1 * var(--top-mask-height) ) calc( -1 * var(--bottom-mask-height) );&lt;/p&gt;
&lt;p&gt;/* Pad the container so content doesn&apos;t hit the masks */
padding-block: var(--top-mask-height) var(--bottom-mask-height);&lt;/p&gt;
&lt;p&gt;/* Apply the masks! */
mask-image: var(--top-mask-image), linear-gradient(transparent var(--top-mask-height), black 0%, black calc( 100% - var(--top-mask-height)), transparent calc( 100% - var(--bottom-mask-height)) ), var(--bottom-mask-image);
mask-repeat: no-repeat;
mask-position: top, top, bottom;
mask-size: 100%, 100%, 100%;&lt;/p&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;.purple-box {
min-height:200px;
background: linear-gradient(to bottom, rebeccapurple, purple);
padding-inline:30px;
padding-block-start:30px;
color:white;
align-content:end;
}&lt;/p&gt;
&lt;p&gt;.purple-box.align-start {
align-content: start;
}&lt;/p&gt;
&lt;p&gt;.mask-box-demo {
background: linear-gradient( 135deg, papayawhip, salmon );
min-height:400px;
display:grid;
place-items:center;
}&lt;/p&gt;
&lt;p&gt;.flags-top {
--top-mask-image: url(&apos;/static/blog/css-masks/mask-flags.svg&apos;);
--top-mask-image-height: 100;
--top-mask-image-width: 1000;
}&lt;/p&gt;
&lt;p&gt;.tent-bottom {
--bottom-mask-image: url(&apos;/static/blog/css-masks/mask-tent-bottom.svg&apos;);
--bottom-mask-image-height: 100;
--bottom-mask-image-width: 1000;
}&lt;/p&gt;
&lt;p&gt;.name-top {
--top-mask-image: url(&apos;/static/blog/css-masks/mask-name-top.svg&apos;);
--top-mask-image-height: 100;
--top-mask-image-width: 1000;
}&lt;/p&gt;
&lt;p&gt;.name-bottom {
--bottom-mask-image: url(&apos;/static/blog/css-masks/mask-name-bottom.svg&apos;);
--bottom-mask-image-height: 100;
--bottom-mask-image-width: 1000;
}
&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;h1&gt;Custom Top and Bottom CSS Container Masks&lt;/h1&gt;
&lt;p&gt;Here&apos;s the use case: You have a design where the top and/or bottom section of a website has a custom shape defining its transition to the container before and/or after it. &lt;em&gt;Essentially, it&apos;s a shape mask on the top or bottom of the container&lt;/em&gt; with the actual content in the section sandwiched in-between.&lt;/p&gt;
&lt;p&gt;Well it turns out that a lot of the methods out there to achieve this effect are pretty old, quite involved, and come with a lot of limitations. Thinking, &lt;em&gt;there &lt;strong&gt;must&lt;/strong&gt; be a better way&lt;/em&gt;, I set out to figure it out.&lt;/p&gt;
&lt;p&gt;I started by consulting the &lt;a href=&quot;https://www.patreon.com/shoptalkshow&quot;&gt;Shop Talk Discord&apos;s&lt;/a&gt; &lt;code&gt;#help-desk&lt;/code&gt; to see if anyone had any thoughts. We mused a bit about clever &lt;code&gt;border-radius&lt;/code&gt; settings and svg &lt;code&gt;clip-path&lt;/code&gt; a bit, but I finally got the spark I needed when &lt;a href=&quot;https://hussein-alhammad.com/&quot;&gt;Hussein Al Hammad&lt;/a&gt; shared a &lt;a href=&quot;https://codepen.io/hus_hmd/pen/qBGNPqw&quot;&gt;very promising demo&lt;/a&gt; using multiple &lt;code&gt;mask-image&lt;/code&gt; definitions!&lt;/p&gt;
&lt;p&gt;It was really a TIL for me that &lt;code&gt;mask-image&lt;/code&gt; supported multiple image definitions, similar to &lt;code&gt;background&lt;/code&gt;, and that was exactly what I needed to formulate an idea for this CSS trick.&lt;/p&gt;
&lt;h2&gt;The Masking Methodology&lt;/h2&gt;
&lt;p&gt;The concept is involved, but not overly complex:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Apply a &lt;code&gt;mask-image&lt;/code&gt; (svg or png) to mask the top of the box.&lt;/li&gt;
&lt;li&gt;Use the ratio of the width and height of the mask image to calculate the mask height based on the viewport or container width.&lt;/li&gt;
&lt;li&gt;Apply a second mask with a linear gradient that starts at the first mask&apos;s height and ends at the bottom mask&apos;s height.&lt;/li&gt;
&lt;li&gt;Apply a third mask to the bottom of the container with the exact same methodology as the top.&lt;/li&gt;
&lt;li&gt;Use the top and bottom heights to create padding and negative margin on the box.&lt;/li&gt;
&lt;li&gt;As a bonus, if you use a container to store your css variables, you can use the calculated mask heights to apply a padding to the containers above and below the masked box.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;One thing to note is that if you&apos;re dealing with full-width containers for this effect, you can use &lt;code&gt;vw&lt;/code&gt; units in your calculations. But in the following example, I opted for the newer &lt;code&gt;cqw&lt;/code&gt; units and defining &lt;code&gt;container-type: inline-size;&lt;/code&gt; on the parent of the &lt;code&gt;.mask-box&lt;/code&gt; which lets you use this concept on containers less than full viewport width! The great thing is that &lt;code&gt;cqw&lt;/code&gt; units are equal to &lt;code&gt;vw&lt;/code&gt; units when not within a container.&lt;/p&gt;
&lt;h2&gt;The .mask-box Code:&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;.mask-container, .mask-box {
  /* The image used as a mask for the top of the container */
  --top-mask-image: url(&apos;data:image/svg+xml,&amp;lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; viewBox=&quot;0 0 1000 100&quot;&amp;gt;&amp;lt;path d=&quot;M0,100S32.06,0,326.72,0c255.72,0,358.35,119.2,673.28,77.47v22.53H0Z&quot;/&amp;gt;&amp;lt;/svg&amp;gt;&apos;);
  --top-mask-image-height: 100; /* The width of the top mask image */
  --top-mask-image-width: 1000; /* The height of the top mask image */

  /* Calculate the height of the top mask */
  --top-mask-height: calc( ( var(--top-mask-image-height) / var(--top-mask-image-width) * 100 ) * 1cqw - 2px );

  /* Define all the same for the bottom */
  --bottom-mask-image: url(&apos;/static/blog/css-masks/swoop-bottom.svg&apos;);
  --bottom-mask-image-height: 100; /* The width of the bottom mask image */
  --bottom-mask-image-width: 1000; /* The height of the bottom mask image */

  /* Calculate the height of the bottom mask */
  --bottom-mask-height: calc( ( var(--bottom-mask-image-height) / var(--bottom-mask-image-width) * 100 ) * 1cqw - 2px );
}

.mask-container { 
  /* Define a container to use cqw instead of vw units */
  container-type: inline-size;

  /* Added bonus: apply padding to container above the .mask-box equal to the negative margin */
  &amp;amp; div:has(+ .mask-box) {
    padding-block-end: var(--top-mask-height);
  }

  /* Added bonus: apply padding to container below the .mask-box equal to the negative margin */
  &amp;amp; .mask-box + div {
    align-items: start;
    padding-block-start: var(--bottom-mask-height);
  }
}

.mask-box {
  /* Apply negative margin to the top and bottom */
  margin-block: calc( -1 * var(--top-mask-height) ) calc( -1 * var(--bottom-mask-height) );

  /* Pad the container so content doesn&apos;t hit the masks */
  padding-block: var(--top-mask-height) var(--bottom-mask-height);
  
  /* Apply the three masks! */
  mask-image: var(--top-mask-image),
              linear-gradient(transparent var(--top-mask-height), black 0%, black calc( 100% - var(--top-mask-height)), transparent calc( 100% - var(--bottom-mask-height)) ),
              var(--bottom-mask-image);
  mask-repeat: no-repeat;
  mask-position: top, top, bottom;
  mask-size: 100%, 100%, 100%; /* You may need to increase the width to 101% on the svg masks to compensate for strange sizing behavior in Firefox */
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rather than piece it together bit by bit, here is the whole thing! I tried to comment each relevant item. A few things to note though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The sibling selectors that add padding to the containers above and below the &lt;code&gt;.mask-box&lt;/code&gt; aren&apos;t required, but it does help if you don&apos;t want to guess or hardcode at where you need to have content stop before it underlaps the mask.&lt;/li&gt;
&lt;li&gt;The masks themselves can be any image, but I find &lt;code&gt;svg&lt;/code&gt; either inlined or referenced as a file to be quite good. For &lt;code&gt;png&lt;/code&gt; images, any transparency in the file will not be part of the mask. The pngs will also tend to get fuzzy as they scale up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the CSS above, you can use your &lt;code&gt;.mask-box&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;mask-container&quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;span&amp;gt;The Content Above the Effect&amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div class=&quot;mask-box&quot;&amp;gt;
    &amp;lt;h3&amp;gt;I am a .mask-box&amp;lt;/h3&amp;gt;
  &amp;lt;/div&amp;gt;

  &amp;lt;div&amp;gt;
    &amp;lt;span&amp;gt;The Content Below the Effect&amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-container&quot;&amp;gt;
&amp;lt;div class=&quot;purple-box&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Above the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-box mask-box-demo&quot;&amp;gt;
&amp;lt;h3&amp;gt;I am a .mask-box&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;purple-box align-start&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Below the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/walpolea/pen/VwOmJRK&quot;&gt;View it on Codepen&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Creating Masks&lt;/h2&gt;
&lt;p&gt;Once this pattern is in place, creating masks is very easy! You can really use any image, though I may recommend a pretty wide sizing ratio, as the mask scales proportionally and may get quite tall on wide screens. For my demos I found a &lt;code&gt;1000px by 100px&lt;/code&gt; sizing was nice!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;You can easily create sets of classes to apply the new images and sizing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.flags-top {
  --top-mask-image: url(&apos;/static/blog/css-masks/mask-flags.svg&apos;);
  --top-mask-image-height: 100;
  --top-mask-image-width: 1000;
}

.tent-bottom {
  --bottom-mask-image: url(&apos;/static/blog/css-masks/mask-tent-bottom.svg&apos;);
  --bottom-mask-image-height: 100;
  --bottom-mask-image-width: 1000;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-container&quot;&amp;gt;
&amp;lt;div class=&quot;purple-box&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Above the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-box flags-top tent-bottom mask-box-demo&quot;&amp;gt;
&amp;lt;h3&amp;gt;I am a .mask-box&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;purple-box align-start&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Below the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.name-top {
  --top-mask-image: url(&apos;/static/blog/css-masks/mask-name-top.png&apos;);
  --top-mask-image-height: 100;
  --top-mask-image-width: 1000;
}

.name-bottom {
  --bottom-mask-image: url(&apos;/static/blog/css-masks/mask-name-bottom.png&apos;);
  --bottom-mask-image-height: 100;
  --bottom-mask-image-width: 1000;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-container&quot;&amp;gt;
&amp;lt;div class=&quot;purple-box&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Above the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;mask-box name-top name-bottom mask-box-demo&quot;&amp;gt;
&amp;lt;h3&amp;gt;I am a .mask-box&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;purple-box align-start&quot;&amp;gt;
&amp;lt;span&amp;gt;The Content Below the Effect&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;And that&apos;s it! This is definitely a CSS trick, but not one that is too overly complicated, especially in comparison to other methods out there. I also like how robust it is. The masks scale responsively very well, and can be varied in size and complexity. You can even use transparent to black gradients on png masks to achieve some interesting fade effects. If you use this technique I would love to see what you do with it, I&apos;m sure it can be pushed even further!&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Surprise for a programmer on Birthday</title><link>https://andrewwalpole.com/blog/surprise-for-a-programmer-on-birthday/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/surprise-for-a-programmer-on-birthday/</guid><description>In 2010, my then girlfriend, now wife, reached out to stack-overflow for coding advice. This is that story.</description><pubDate>Fri, 03 May 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Surprise for a programmer on Birthday&lt;/h1&gt;
&lt;p&gt;Was the title of a &lt;a href=&quot;https://stackoverflow.com/questions/2420689/surprise-for-a-programmer-on-birthday&quot;&gt;stack-overflow post&lt;/a&gt; from 2010 that no longer exists. If you try to visit you&apos;ll see:&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;It was posted by my then-cool-girlfriend, now-incredible-wife as a genuine and innocent question about code. Little did she know the contentious pretension and division she was about to unleash upon the stack-overflow community.&lt;/p&gt;
&lt;h2&gt;The Story&lt;/h2&gt;
&lt;p&gt;Unbeknownst to me, a few months before my 25th birthday, she cleverly posted the question under a pseudonym:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3&gt;Surprise for a programmer on Birthday&lt;/h3&gt;
&lt;p&gt;Help! My boyfriend&apos;s birthday is next month. Since he is a programmer, I&apos;d love to make him a cake with the code for &quot;happy birthday&quot; (and perhaps something awesome) written in icing on top. Not being a programmer myself, I have no idea where to begin. Any suggestions would be greatly appreciated!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Within hours the post went viral. Lots of upvotes, answers and comments poured in. For the most part it was a fun time!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A programmer with a girlfriend? - Bob Kaufman&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The cake is a lie! - OMG Ponies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;One-twelth of the programmers on this site (approx 12,000 people) have a birthday next month. Now all of them who have girlfriends are going to expect an awesome cake with code. Two of those three programmers are going to be very disappointed. - Jeffrey L Whitledge&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;I think Jeffrey&apos;s count is wrong. I remember a programmer I used to work with, and she had a girlfriend. - Windows programmer&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Very amusingly, many questioned the authenticity of the post and OP entirely:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I call B.S. on this whole question. This so-called &quot;programmer&apos;s girlfriend&quot;, leading us on with this &quot;penelope&quot; handle, must know more than she&apos;s letting on, if she&apos;s posting on StackOverflow. - Jeff Meatball Yang&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Why are you all assuming that &quot;Penelope&quot; is not a guy? - Vicky&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;There is statistical and conclusive proof she is NOT a programmer and is NOT a guy - she responded to every post (as far as I could find) - Mark Schultheiss&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But as quickly as the party started, so too the sticklers arrived:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This question has been closed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The question was voted for closure. You see, this wasn&apos;t the first cake question to cause a tizzy. A similar question had previously been asked proposed a wedding cake, causing strict community guidelines to be put in place about the content and intention of the questions posted on stack-overflow. Fun? Girlfriends and boyfriends? Cake? &quot;No thanks!&quot; said stack-overflow.&lt;/p&gt;
&lt;p&gt;But in the democratic spirit that the platform provided, the crowd fought back!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Voting to reopen just cause this question is awesome. - Earlz&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;People closing this lovely question, you do realize that if you get the required votes, this question will be immediately re-opened by hordes of fans? - JSBangs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;My only gripe for this question is it definitely should&apos;ve been CW, but thats not enough for it to be closed. Surely we can have some fun on here every once in a while :) - Earlz&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;@haters - this question stays! - pokstad&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The question was voted to be reopened!&lt;/p&gt;
&lt;p&gt;And closed again!&lt;/p&gt;
&lt;p&gt;Back up!&lt;/p&gt;
&lt;p&gt;The war raged on for days. Users chimed in, some now incredulous that this one question had earned her account more reputation points and gold badges than many of them had accumulated in years of platform use.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LIGHTEN UP PEOPLE! This is starting to tick me off. If it bothers you that much that penelope has a few gold stickers more than you...you have some major issues. - Epaga&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After the main hubbub subsided, I was let in early on the fun. It ended up being the best gift of all to see such an amusing event in internet history caused on my behalf by my better half.&lt;/p&gt;
&lt;p&gt;Cake baked and party concluded, a final update was made, with pictures as proof:&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3&gt;UPDATE:&lt;/h3&gt;
&lt;p&gt;hey everyone! sorry it has taken me so long to post pics. the cake was a HUGE success and he enjoyed every bite. but most of all, he loved the &quot;actionscript birthday message&quot; that graced the top of it! i wanted to thank you all for your suggestions. i have printed them and will make many more &quot;coded&quot; cakes as the holidays go by. i really want to do the heart &quot;equation&quot; for valentines day! i took peter toroks idea and tailored it for andrew&apos;s birthday cake (i asked a programmer to help me make it fit on the cake!). needless to say, andrew and all of his programmer buddies got a kick out of it and gobbled up the dark chocolate, peanut butter cake that was underneath all that nerdy icing-y goodness! here is proof:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;gallery&quot;&amp;gt;
&amp;lt;img class=&quot;wide&quot; src=&quot;/cake/CIMG0157_w800.jpg&quot; alt=&quot;The cake!&quot;&amp;gt;
&amp;lt;img src=&quot;/cake/CIMG0168_w800.jpg&quot; alt=&quot;Tania (me!) with my creation!&quot;&amp;gt;
&amp;lt;img src=&quot;/cake/CIMG0162_w800.jpg&quot; alt=&quot;Andrew with his cake&quot;&amp;gt;
&amp;lt;img class=&quot;wide&quot; src=&quot;/cake/CIMG0172_w800.jpg&quot; alt=&quot;Binary birthday candles!&quot;&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;so thank you ALL again! i had a lot of fun making the cake and thanks to all of you, he&apos;ll never forget it, or this birthday! -tania&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;With the question&apos;s utility brought to an end, it was closed and locked for good. Sometime later it was removed altogether. I used the &lt;a href=&quot;https://web.archive.org/web/20100322195455/http://stackoverflow.com:80/questions/2420689/surprise-for-a-programmer-on-birthday&quot;&gt;wayback machine&lt;/a&gt; and &lt;a href=&quot;http://www.stackprinter.com/questions/surprise-for-a-programmer-on-birthday.html&quot;&gt;stack printer&apos;s archive&lt;/a&gt; to piece the story back together and create this more official retelling. Check those links if you want to read through all of the other comments and answers given.&lt;/p&gt;
&lt;p&gt;About a year later, the story was referenced in &lt;a href=&quot;https://donkirkby.blogspot.com/2011/05/birthday-cakes-for-programmers.html&quot;&gt;a blog post about code-decorated cakes&lt;/a&gt; and a few years ago I attempted to archive an abbreviated version of this story as a &lt;a href=&quot;https://x.com/walpolea/status/1289077044869881858&quot;&gt;twitter thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, I&apos;ll go ahead and leave you with this &lt;a href=&quot;https://codepen.io/walpolea/pen/PogvaJp&quot;&gt;codepen recreation of the cake&lt;/a&gt; itself, running the code on top of it:&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;codepen&quot; data-height=&quot;800&quot; data-default-tab=&quot;result&quot; data-slug-hash=&quot;PogvaJp&quot; data-user=&quot;walpolea&quot; style=&quot;height: 800px; 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/walpolea/pen/PogvaJp&quot;&amp;gt;
Surprise for a programmer on Birthday&amp;lt;/a&amp;gt; by Andrew (&amp;lt;a href=&quot;https://codepen.io/walpolea&quot;&amp;gt;@walpolea&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;&amp;lt;style&amp;gt;&lt;/p&gt;
&lt;p&gt;blockquote, blockquote &amp;gt; * {
font-size: 90% !important;
}&lt;/p&gt;
&lt;p&gt;.gallery {
margin-inline:Auto;
max-width:800px;
display:grid;
grid-template-columns: 1fr 1fr;&lt;/p&gt;
&lt;p&gt;.wide {
grid-column: 1/ -1;
}
}&lt;/p&gt;
&lt;p&gt;&amp;lt;/style&amp;gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Opinions for Writing Good CSS</title><link>https://andrewwalpole.com/blog/opinions-for-writing-good-css/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/opinions-for-writing-good-css/</guid><description>CSS is a flexible language that, in my opinion, requires opinions to be written well and consistently. Here are some of my opinions for writing good CSS.</description><pubDate>Tue, 11 Jun 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Opinions for Writing Good CSS&lt;/h1&gt;
&lt;p&gt;CSS can be hard and frustrating for beginners. The nature of the language is so different from traditional programming languages. While it’s easy to learn the parts: &lt;code&gt;selectors&lt;/code&gt;, &lt;code&gt;properties&lt;/code&gt;, &lt;code&gt;etc.&lt;/code&gt; It&apos;s much tougher to practically compose multiple ideas together to make something new or more complex happen.&lt;/p&gt;
&lt;p&gt;There just aren’t a lot of straight paths in CSS. With multiple ways to accomplish the same concepts, it can be difficult to figure out a solution that is new to you, and also to know if it’s the right or best approach. &lt;em&gt;If &quot;it depends&quot; is a telling mantra of the senior dev, CSS is indeed a language written to fill that space in an experienced developer&apos;s heart.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;As hard as it might be to fill the knowledge gap of knowing when and where to reach for what, that&apos;s what makes it such a beautiful language. CSS is quite robust and capable, as individual declarations can be complexly composed together into a lovely symphony of style and layout.&lt;/p&gt;
&lt;p&gt;One of my core pieces of advice is: &lt;a href=&quot;/blog/to-be-flexible-be-inflexible&quot;&gt;to be flexible, be inflexible&lt;/a&gt;. And I think that&apos;s really what this post is about with regard to CSS. Due to the nature of how complex CSS can and should be when written, it&apos;s important, to perhaps, &lt;strong&gt;essential&lt;/strong&gt; to &lt;em&gt;have opinions&lt;/em&gt; about how you write it.&lt;/p&gt;
&lt;h2&gt;Opinions&lt;/h2&gt;
&lt;p&gt;As someone who has taught CSS to students and junior developers, take them or leave them, these are some of the opinions I&apos;ve built up that I think help you in writing good CSS from day one.&lt;/p&gt;
&lt;h3&gt;Don&apos;t include CSS that isn&apos;t needed&lt;/h3&gt;
&lt;p&gt;Too obvious? Maybe not! It&apos;s often necessary to experiment with things – don&apos;t be afraid of that! – but clean up failed experiments right away. Once you let something unnecessary linger in the codebase no one in the future will be able to easily tell if it&apos;s necessary or not – not even you!&lt;/p&gt;
&lt;h3&gt;Use comments&lt;/h3&gt;
&lt;p&gt;Specifically, sometimes you need to write some CSS that either you aren&apos;t 100% confident about why you need it. This is the perfect place to express that skepticism in a comment, both for you and anyone else reviewing the code in the future. While there are other good reasons to use comments, including this confidence metadata for a solution opens up quick-win opportunities to revisit things later in an effort to refactor and improve the codebase. It also may signal spots where tough-to-track bugs may appear later.&lt;/p&gt;
&lt;h3&gt;Avoid using Ids for CSS selection&lt;/h3&gt;
&lt;p&gt;Embrace the cascade. In doing that, you&apos;ll find that one of the core concepts is being adept at managing specificity in your code. And in doing that, the Id selector is just too potent to get used to using. For more information about this I can&apos;t recommend enough &lt;a href=&quot;https://www.youtube.com/watch?v=8Z8H2NEbLtE&quot;&gt;this recent talk&lt;/a&gt; by &lt;a href=&quot;https://www.mayank.co/&quot;&gt;Mayank&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Avoid using !important&lt;/h3&gt;
&lt;p&gt;Also related to managing specificity well, &lt;code&gt;!important&lt;/code&gt; is a code smell. While there are some rare occasions where you absolutely need it (especially with inherited or older codebases), if you find yourself using it, there is a bigger issue somewhere. Stop and search out the issue first before deciding it is the solution.&lt;/p&gt;
&lt;h3&gt;Figure out a system for consistency&lt;/h3&gt;
&lt;p&gt;No system is a bad system, and half a system is still a system. Naming, structure, selecting, vars, nesting, scoping; there are so many places in CSS where systematic consistency will go far in making your code revisitable and maintainable. Have a system of any kind, document it, and use it. As far as what system you should use, that’s all up to you. Do what makes sense. From &lt;a href=&quot;https://getbem.com/&quot;&gt;BEM&lt;/a&gt; to &lt;a href=&quot;https://cube.fyi/&quot;&gt;CUBE&lt;/a&gt; to making it up on the spot, there are a lot of options and most aren’t wrong considering the alternative.&lt;/p&gt;
&lt;h3&gt;Resets and defaults are useful&lt;/h3&gt;
&lt;p&gt;In a related vein to systematizing, a good reset can take some antiquated complexities out of starting with CSS. I recommend checking out &lt;a href=&quot;https://piccalil.li/blog/a-more-modern-css-reset/&quot;&gt;Andy Bell&apos;s&lt;/a&gt; and &lt;a href=&quot;https://www.joshwcomeau.com/css/custom-css-reset/&quot;&gt;Josh Comeau&apos;s&lt;/a&gt; modern reset ideas.&lt;/p&gt;
&lt;p&gt;Defaults are similar to resets, but can be used in a few clever ways. Define default values globally. These can serve your system as smart fallbacks. But also define defaults on things you can&apos;t afford to miss. The classic example being to select and apply a border to all images that have no &lt;code&gt;alt&lt;/code&gt; attribute defined:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;img:not([alt]) {
  border: 5px solid red;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clever selections of things you should be attending to aren&apos;t just a party trick, they work for you in complex builds, serving as a reminder to enforce your rules and system.&lt;/p&gt;
&lt;h3&gt;Understand the codebase&lt;/h3&gt;
&lt;p&gt;This might be more specific to working on a team, but it&apos;s important to spend extra time reading and internalizing the CSS you aren&apos;t writing in a project. Aligning your coding style and the tools you reach for to the system in place is essential to keep the codebase feeling cohesive as you pitch in to build it. Get to know your reset, utility classes, global CSS vars, typography, colors, and spacing systems before moving forward on contributing your own components and styles.&lt;/p&gt;
&lt;h3&gt;Lean heavily on CSS Variables&lt;/h3&gt;
&lt;p&gt;CSS variables aren&apos;t just about saving you keystrokes. Set up CSS variables as if you&apos;re gathering up your tools to implement your design. &quot;I know I&apos;ll need these colors, backgrounds, pixel values, etc.&quot; Doing this will start to form a cohesive API of what makes the thing you&apos;re styling its specific style. In most cases you&apos;ll be set up to play with values easily and customize styling and layout across variations of your subject.&lt;/p&gt;
&lt;h3&gt;Assess responsive requirements of a design first&lt;/h3&gt;
&lt;p&gt;Before writing any CSS, especially layout CSS, have a good long sit-down with your design. It&apos;s essential to formulate an understanding of how the next thing you&apos;re going to build behaves responsively. Clear responsive understanding will lead to cleaner, more elegant and thoughtful CSS solutions. Conversely, attempting to apply layout changes to a subject after one specific sizing has been created can often lead to brittle, band-aided solutions.&lt;/p&gt;
&lt;h3&gt;Avoid margins on components and padding on containers&lt;/h3&gt;
&lt;p&gt;I&apos;m not in the &lt;code&gt;no margins ever&lt;/code&gt; club, but I do align with some of those points. Margin is special in that it often conveys spacing definitions between items. In a mode where you&apos;re styling a component, which often does not yet have an established context, you will want to wait to define a margin until the component is instanced into a final context.&lt;/p&gt;
&lt;p&gt;Similarly, in styling something meant to contain or slot in some other unknown contextual item, be careful not to fix yourself to a specific set of padding so that it can be appropriately defined in each instance used.&lt;/p&gt;
&lt;p&gt;There are exceptions to this depending on how flexible your system or design needs to be, but it helps to start from a cautious mindset and then explicitly opt-in to marrying spacing to any component.&lt;/p&gt;
&lt;h3&gt;Don&apos;t paste CSS&lt;/h3&gt;
&lt;p&gt;I can&apos;t emphasize this enough, especially for the junior devs. &lt;strong&gt;Do. Not. Paste. CSS.&lt;/strong&gt; Co-pilot for CSS? &lt;em&gt;No.&lt;/em&gt; Figma styles for CSS? &lt;em&gt;No.&lt;/em&gt; Grab all those cool styles from someone else&apos;s component? &lt;em&gt;No.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Can you be informed by all of those things? &lt;em&gt;&lt;strong&gt;Yes! Absolutely!&lt;/strong&gt;&lt;/em&gt; But resist the urge to copy and paste large blocks of CSS as writing out each property definition will both make you a better CSS developer and engage your brain in a way that allows you to always make explicit and understood choices about the CSS you are writing.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;I&apos;m sure I could continue to build up this list until it matched my personal style of writing CSS. If you&apos;ve disagreed with any of these, that&apos;s fine, they&apos;re just my opinions. But I do think I&apos;ve picked a set that can be generally agreed upon and that I&apos;ve seen provide a solid base for helping folks simplify their approach to CSS and end up with cleaner, better code. And I hope this post begs the question, &lt;em&gt;What CSS opinions do you have?&lt;/em&gt;&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Build Your Next App with Durable Redis</title><link>https://andrewwalpole.com/blog/build-your-next-app-with-durable-redis/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/build-your-next-app-with-durable-redis/</guid><description>Capability delivered through simplicity is one of my favorite features of tech. Digging into using durable redis to power a web-application, I found that it hits a perfect sweet spot of being easy to work with and capable of storing and relating diverse application data.</description><pubDate>Fri, 01 Nov 2024 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Build Your Next App with Durable Redis&lt;/h1&gt;
&lt;p&gt;Connecting your website or app to a database unlocks an immense amount of possibilities; being able to create something that adapts to usage over time is a critical enabling factor to compounding work that a computer can do for a person.&lt;/p&gt;
&lt;p&gt;In the past I&apos;ve written at length about a few of the KV store offerings out there, like &lt;a href=&quot;/blog/building-a-like-button-with-cloudflare-workers/&quot;&gt;Cloudflare KV&lt;/a&gt; and &lt;a href=&quot;/blog/fast-and-simple-apis-with-deno-kv/&quot;&gt;Deno KV&lt;/a&gt;. I like them because because they are some of the simplest data services that you can tap into to bring your projects up to that next level of being dynamically data-driven.&lt;/p&gt;
&lt;p&gt;With simple keys and values though, there are limitations around how far you can push those stores into behaving like a fully modeled application database. You might think then, that as you need to graduate up to another level of database capability you may need to bite the bullet and jump into something with way more setup overhead like Postgres, MongoDB or some similar equivalent.&lt;/p&gt;
&lt;h2&gt;What&apos;s Redis?&lt;/h2&gt;
&lt;p&gt;I&apos;ll be honest, for as long as Redis has been around, I&apos;ve mostly been oblivious to it. Academically I get it: &lt;em&gt;It&apos;s a database that capitalizes on the speed of running in-memory to make getting and setting data fast.&lt;/em&gt; So as a proxy layer that sits between your big slow database and your user, it can be applied into your architecture as a means to squeeze out performance and scalability of your system.&lt;/p&gt;
&lt;p&gt;Given that summation, it&apos;s hopefully apparent that Redis really doesn&apos;t sound like something to be reaching for for small projects and experiments.&lt;/p&gt;
&lt;h2&gt;What&apos;s Durable Redis&lt;/h2&gt;
&lt;p&gt;Here&apos;s where that conclusion flips on its head a bit.&lt;/p&gt;
&lt;p&gt;Redis is built with the ability to be hosted as a durable solution. While usually it isn&apos;t when used in the proxying context described above, durability is really what Redis lacks to be a stand-alone database solution. Essentially this means that as Redis is interacted with in-memory, it also performs the work of syncing and saving data to disk, allowing an outage to not cause total loss of data. This may slow down performance a bit, but trades off excellently with the need to simplify your architecture and choose Redis as a primary data source.&lt;/p&gt;
&lt;p&gt;Durable Redis is pretty cool. The database itself is NoSQL in concept, bringing the simplicity of a KV store, but also going beyond with having an understanding of many different value types which can be traversed, compiled, searched and queried quite powerfully.&lt;/p&gt;
&lt;h2&gt;How to get started with Durable Redis&lt;/h2&gt;
&lt;p&gt;I was introduced to Redis through &lt;a href=&quot;https://vercel.com/docs/storage/vercel-kv&quot;&gt;Vercel KV&lt;/a&gt;. Digging in there though, you quickly realize that the service is mostly a light wrapper around &lt;a href=&quot;https://upstash.com/&quot;&gt;Upstash&lt;/a&gt;, one of the only hosted durable Redis services I&apos;ve been able to find. If you&apos;re looking to host it yourself, this really isn&apos;t the article for you, but I imagine it could be figured out pretty well with Docker.&lt;/p&gt;
&lt;p&gt;Maybe worth mentioning also that &lt;a href=&quot;https://aws.amazon.com/memorydb/&quot;&gt;Amazon has MemoryDB&lt;/a&gt; which touts itself as a Redis compatible, durable data solution; I haven&apos;t tried it out but it sounds possibly comparable.&lt;/p&gt;
&lt;p&gt;Upstash has quite decent &lt;a href=&quot;https://upstash.com/pricing&quot;&gt;pay-as-you-go pricing&lt;/a&gt;, and a generous free tier, making it ideal for side-projects or small apps.&lt;/p&gt;
&lt;h3&gt;The basics&lt;/h3&gt;
&lt;p&gt;Upstash walks you through setting up your first database, and when finished provides you the tokens you need to access the database via their SDK which comes in the usual various flavors.&lt;/p&gt;
&lt;p&gt;For me, I&apos;ve been using it through Astro SSR, which allows me to easily build server-side application logic that can interact with the DB, or create API endpoints that can proxy access to the client-side.&lt;/p&gt;
&lt;p&gt;Essentially, like any KV store, Redis allows you to set data at keys; up to 250 million of them per instance! So even simple sets and gets will go quite far for you. Let&apos;s take a look at simple CRUD functions before talking about some useful capabilities:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;redis-crud.js&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import { Redis } from &apos;@upstash/redis&apos;

const redis = new Redis({
  url: &apos;https://your-db-instance-name.upstash.io&apos;,
  token: &apos;********&apos;,
})

/**
 * Create or Update a JSON object using `JSON.SET`.
 */
export async function createOrUpdateObject(key, data, path = &apos;$&apos;) {
  return await redis.json.set(key, path, data); // &apos;$&apos; indicates the root path
}

/**
 * Retrieve a JSON object by key using `JSON.GET`.
 */
export async function getObject(key, path = &apos;$&apos;) {
  return await redis.json.get(key, path); // &apos;$&apos; retrieves the entire object
}

/**
 * Delete an object using `DEL`.
 */
export async function deleteObject(key, path = &apos;$&apos;) {
  return await redis.json.del(key, path);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn&apos;t get much easier than that folks! Four critical operations handled in three simple functions.&lt;/p&gt;
&lt;p&gt;If you noticed the &lt;code&gt;path = &apos;$&apos;&lt;/code&gt; bit, essentially this has to do with Redis understanding JSON to a fair extent. We can use the &lt;a href=&quot;https://upstash.com/docs/redis/sdks/ts/commands/json/set&quot;&gt;JSON commands&lt;/a&gt; to modify objects and arrays and nested values within sub-parts of object models rather than continuously retrieving and re-storing large objects over each call.&lt;/p&gt;
&lt;h3&gt;A few Redis tips&lt;/h3&gt;
&lt;p&gt;To take Redis further, I found the pattern of adding types to my keys very useful. For example, say you have multiple types of data you want to store in a Redis database. Well, the top-level set of keys are flat, so you can use the keys themselves to delineate data.&lt;/p&gt;
&lt;p&gt;Keys like, &lt;code&gt;user:12345&lt;/code&gt; or &lt;code&gt;todo:98765&lt;/code&gt; where the prefix defines the data type and the suffix defines the Id, allows for a great way to scope down your data sets. You can then use &lt;code&gt;SCAN&lt;/code&gt; which accepts a glob-style pattern to query your keys by type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function getAllObjectsOfType(type) {

  const pattern = `${type}:*`;
  let cursor = 0;
  let keys = [];
  
  //Use SCAN to retrieve all the keys
  do {
    const [nextCursor, scanKeys] = await redis.scan(cursor, { match: pattern });
    cursor = parseInt(nextCursor);
    keys = keys.concat(scanKeys);
  } while (cursor !== 0);
  
  if (keys.length === 0) return [];
  
  //Use JSON.MGET to retrieve all the objects at those keys:
  return await redis.json.mget(keys, &apos;$&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can write a simple function to check if a key exists:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export async function keyExists(key) {
  return await redis.exists(key);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also write functions to modify JSON primitives, like adding/removing items in arrays, flipping boolean values, and incrementing or decrementing numeric values. Here are a few examples of those:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
export function redisKey(type, objectId) {
  return `${type}:${objectId}`;
}

/**
 * Toggle a boolean field in a JSON object using `JSON.TOGGLE`.
 */
export async function toggleBooleanField(type, objectId, path) {
  const key = redisKey(type, objectId);

  // Use `JSON.TOGGLE` to toggle the boolean field
  const newValue = await redis.json.toggle(key, path);

  // Return the new boolean value
  return newValue;
}


/**
 * Increment a numeric field in a JSON object using `JSON.NUMINCRBY`.
 */
export async function incrementObjectField(type, objectId, path, incrementBy) {
  const key = redisKey(type, objectId);
  return await redis.json.numincrby(key, path, incrementBy);
}

/**
 * Append a value to an array in a JSON object using `JSON.ARRAPPEND`.
 */
export async function appendToArray(type, objectId, path, value) {
  
  const key = redisKey(type, objectId);
  const exists = await redis.exists(key);
  
  if (!exists) {
    return await redis.json.set(key, path, [value]);
  } else {
    return await redis.json.arrappend(key, path, value);
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I&apos;ve just got done with my first foray into Durable Redis while building an app for a client using Astro and Vercel KV. I was impressed with the sweet spot of capability and simplicity in getting up and running with it, and hopefully I&apos;ve shown you that this is a great option for projects that are not overly complex but need a bit more than a simple KV or object store. I think this is a great option to help get your next fun idea off the ground and even scale up a bit as it grows into something bigger.&lt;/p&gt;
</content:encoded><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item><item><title>Audit Content Reads with Cloudflare KV</title><link>https://andrewwalpole.com/blog/audit-content-reads-with-cloudflare-kv/</link><guid isPermaLink="true">https://andrewwalpole.com/blog/audit-content-reads-with-cloudflare-kv/</guid><description>A quick write up on how Astro made it very easy to track page reads in Cloudflare KV.</description><pubDate>Mon, 27 Feb 2023 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Audit Content Reads with Cloudflare KV&lt;/h1&gt;
&lt;p&gt;Back when I built my current site in Eleventy, I &lt;a href=&quot;/blog/building-a-like-button-with-cloudflare-workers&quot;&gt;talked about how to use petite-vue and Cloudflare KV&lt;/a&gt; to create a like button for your webpage. &lt;em&gt;Look! There it is over on the bottom right of this page (sorry RSS folks), maybe you should click it to make sure it works!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To this day, that post is quite popular and holds up well if you want to follow along with the KV and edge function setup.&lt;/p&gt;
&lt;p&gt;Recently, I used the same concept to track page reads on my site. What&apos;s a page read? Well, for me it&apos;s just that you&apos;ve scrolled to the end of the page. This will let me audit, very generally, how many folks are engaging with my posts and getting to the end.&lt;/p&gt;
&lt;p&gt;The new part of this setup was no longer creating something waiting for user interaction. Instead, I needed something to trigger when it came into view at the end of the page. While rolling your own &lt;code&gt;IntersectionObserver&lt;/code&gt; isn&apos;t too painful, there was an even better solution at hand.&lt;/p&gt;
&lt;h2&gt;Astro Islands and client:visible&lt;/h2&gt;
&lt;p&gt;Astro&apos;s islands architecture treats your UI components as build-time rendered by default. When you want to make a client-side component interactive with JavaScript, you need to explicitly add a directive to it to tell Astro to hydrate it. One of the directives is &lt;code&gt;client:visible&lt;/code&gt; which handles all the &lt;code&gt;IntersectionObserver&lt;/code&gt; setup automatically.&lt;/p&gt;
&lt;p&gt;So all I needed to do was build a Vue component that &lt;code&gt;onMounted()&lt;/code&gt; pings my edge function read counter, and that&apos;s it! Here&apos;s a look at that Vue component:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div style=&quot;visibility:hidden;&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

&amp;lt;script setup&amp;gt;

import { ref, onMounted } from &quot;vue&quot;;

const endpoint = &apos;https://pagereader.walpolea.workers.dev/&apos;;
const {title} = defineProps([&apos;title&apos;]);
const reads = ref(undefined);

const postRead = async (title) =&amp;gt; {

  if(title) {
    const response = await ( await fetch(endpoint, {
      method: &apos;POST&apos;,
      headers: {
        &apos;Accept&apos;: &apos;application/json&apos;,
        &apos;Content-Type&apos;: &apos;application/json&apos;,
      },
      body: JSON.stringify({title})
    })).json();

    reads.value = response.reads;
  } 
}

onMounted( () =&amp;gt; {
  postRead( title );
});

&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The component is comprised of a hidden empty &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and takes a title prop, which &lt;code&gt;onMounted()&lt;/code&gt;, is used to ping the &lt;code&gt;pagereader&lt;/code&gt; service.&lt;/p&gt;
&lt;p&gt;And here is how it&apos;s used with the &lt;code&gt;client:visible&lt;/code&gt; directive:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;PageReader title={Astro.url.pathname} client:visible /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my Cloudflare dashboard I can view the KV store to see my current counts.&lt;/p&gt;
&lt;p&gt;There&apos;s nothing too ground-breaking here, but analytics platforms these days are often heavy and intrusive. On the flip side, server-based analytics have a terrible time distinguishing between bot and meat traffic. So I quite like this as a simple middle-ground solution that, thanks to Astro, was very easy to put in place.&lt;/p&gt;
</content:encoded><enclosure url="https://pub-40fccf9e493a4d029eb2b8955f358ac3.r2.dev/audit-content-reads-with-cloudflare-kv.mp3" type="audio/mp3"/><author>andrew@andrewwalpole.com (Andrew Walpole)</author></item></channel></rss>