<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="pretty-atom-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  
  <title>JSON feed for my blog by Matt Hobbs (Nooshu.com)</title>
  <subtitle>This is the website of Matt Hobbs, who is a Frontend Engineering Manager from Oxfordshire, UK.</subtitle>
  <link href="https://nooshu.com/feed/feed-atom.xml" rel="self" />
  <link href="https://nooshu.com/" />
  <updated>2026-06-20T00:00:00Z</updated>
  <id>https://nooshu.com/</id>
  <author>
    <name>Matt Hobbs</name>
  </author>
  <entry>
    <title>How a Simple CSP Tweak Turned into an AI Project</title>
    <link href="https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/" />
    <updated>2026-06-20T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;In this post, I’ll tell the story of how a stray semicolon led me to build an AI-powered tool to make creating Content Security Policies (CSPs) a bit less painful for developers.&lt;p&gt;I’ve written about CSPs on this blog before:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2024/12/28/securing-your-static-website-with-http-response-headers/&quot;&gt;&lt;strong&gt;December 2024&lt;/strong&gt;: Securing your static website with HTTP response headers&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/&quot;&gt;&lt;strong&gt;February 2025&lt;/strong&gt;: Configuring your Content-Security-Policy on your development environment in 11ty&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;To quote from my 2024 post:&lt;blockquote&gt;&lt;p&gt;The Content Security Policy (CSP) header is a security feature that protects web applications from attacks like Cross-Site Scripting (XSS) and code injection by controlling the sources from which browsers can load and execute content. If there&#39;s only a single response header you implement today, then make it the CSP Response header.&lt;/blockquote&gt;&lt;p&gt;The post has a few more details, so if you are interested you can &lt;a href=&quot;https://nooshu.com/blog/2024/12/28/securing-your-static-website-with-http-response-headers/#content-security-policy-csp&quot;&gt;find the Content-Security-Policy (CSP) section here&lt;/a&gt;.&lt;p&gt;As mentioned in the post, there are already a huge number of tools available on the web for creating CSP’s, but none of them really fit my needs. So, I decided to write my own tool using &lt;a href=&quot;https://cursor.com/referral?code=XDKDHWAJX4RJ&quot;&gt;cursor.ai&lt;/a&gt; and host it on the web for everyone to evaluate and use. &lt;strong&gt;Note&lt;/strong&gt;: I&#39;ve included my referral link so any readers can get 50% off their first month subscription.&lt;p&gt;It is called &lt;a href=&quot;https://csp-playground.dev/&quot;&gt;CSP Playground&lt;/a&gt;⁠. Despite the name, writing a CSP is rarely described as “fun”, and “playground” may be overselling the experience somewhat.&lt;p&gt;It seemed as sensible a name as any, though. Besides, the domain wasn’t taken and only cost £9, which is cheap enough to make almost any naming decision seem justified!&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-300.webp 300w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-600.webp 600w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-814.webp 814w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-1100.webp 1100w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Using Cloudflare Registrar I got a quote for csp-playground.secuity, only $2000.20 per year!&quot; decoding=&quot;async&quot; height=&quot;147&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-300.png 300w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-600.png 600w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-814.png 814w, https://nooshu.com/blog/2026/06/20/how-a-simple-csp-tweak-turned-into-an-ai-project/eRJUnGcXhx-1100.png 1100w&quot; width=&quot;1100&quot;&gt;&lt;/picture&gt;&lt;p&gt;I briefly considered splashing out on a &lt;code&gt;.security&lt;/code&gt; top-level domain (TLD), until &lt;a href=&quot;https://www.cloudflare.com/en-gb/products/registrar/&quot;&gt;Cloudflare Registrar&lt;/a&gt; reminded me that the privilege would cost &lt;strong&gt;only £1,515 per year&lt;/strong&gt;.&lt;p&gt;I suspect telling my wife that we’ve cancelled our summer holiday plans so I can own a particularly niche bit of the internet would have tested the “for richer or poorer” part of our vows rather more thoroughly than intended. 😬&lt;h2 id=&quot;problem&quot;&gt;Problem&lt;/h2&gt;&lt;p&gt;If any readers have tried to write a CSP “manually”, you will understand how painful it really is! Understanding the very particular syntax is challenging. Although CSP itself does not have to be written on a single line conceptually, when the header is sent over the network, it must not contain any arbitrary line breaks, else it will be classed as invalid. And don’t even get me started on the use of &lt;code&gt;;&lt;/code&gt;, one misplaced semicolon and your browser console will throw a very red looking hissy fit!&lt;p&gt;There are whole RFC’s dedicated to the specifics of HTTP header fields, that I won’t even pretend to understand! Linked below for a little light reading, if you are struggling to sleep!&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7231&quot;&gt;RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.rfc-editor.org/info/rfc9110/&quot;&gt;RFC 9110 - HTTP Semantics&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc9112&quot;&gt;RFC 9112 - HTTP/1.1&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;Anyway, as I always do, I’ve wandered off on a bit of a tangent.&lt;p&gt;What I’m basically saying is writing CSP’s is complicated and frustrating (especially for a terrible typist like myself!), So why not make life easier and spend a couple of days in &quot;AI world” and build a tool to make your life easier? You never know, it may even help others too!&lt;h2 id=&quot;features&quot;&gt;Features&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Form-based editor for every standard CSP directive&lt;/strong&gt;: No hand-editing of semicolon syntax&lt;li&gt;&lt;strong&gt;Import from URL&lt;/strong&gt;: Fetch a policy from headers or meta tags, or detect when none exists&lt;li&gt;&lt;strong&gt;Paste headers or policy&lt;/strong&gt;: Paste a full HTTP response header block or a single &lt;code&gt;Content-Security-Policy&lt;/code&gt; line; the CSP is extracted in the browser and used to pre-fill the form&lt;li&gt;&lt;strong&gt;Real-time security score&lt;/strong&gt;: Including actionable recommendations that navigate you instantly to the exact directive&lt;li&gt;&lt;strong&gt;Nonce and hash helpers&lt;/strong&gt;: Generate values and copy ready-to-paste HTML snippets&lt;li&gt;&lt;strong&gt;One-click export&lt;/strong&gt;: Policy, header line, or server config for Apache, Nginx, Caddy, Cloudflare, Netlify, Vercel, and more&lt;li&gt;&lt;strong&gt;Report-only mode&lt;/strong&gt;: Option to output the CSP using &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt;. Allowing for testing before enforcement&lt;li&gt;&lt;strong&gt;MDN links and inline help&lt;/strong&gt;: Every directive and sandbox flag links directly to the relevant MDN content&lt;li&gt;&lt;strong&gt;Privacy focused by design&lt;/strong&gt;: No accounts, no tracking, and no database. Nothing is stored because there’s nowhere to store it&lt;li&gt;&lt;strong&gt;Runs mostly in the browser&lt;/strong&gt;: Policy editing and paste import stay local; the only server-side processing happens when you explicitly import a URL to fetch your site’s CSP&lt;li&gt;&lt;strong&gt;Free and open-source&lt;/strong&gt;: &lt;a href=&quot;https://choosealicense.com/licenses/mit/&quot;&gt;MIT licensed&lt;/a&gt; at &lt;a href=&quot;https://csp-playground.dev/&quot;&gt;https://csp-playground.dev/&lt;/a&gt;, &lt;a href=&quot;https://github.com/Nooshu/CSP-Playground&quot;&gt;code available on GitHub&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Clean design&lt;/strong&gt; Built on the fantastic &lt;a href=&quot;https://utopia.fyi&quot;&gt;utopia.fyi&lt;/a&gt; Fluid Design CSS grid built by &lt;a href=&quot;https://clearleft.com/&quot;&gt;Clearleft&lt;/a&gt;&lt;/ul&gt;&lt;h3 id=&quot;audit&quot;&gt;Audit&lt;/h3&gt;&lt;p&gt;Start from a real policy instead of a blank form. Enter a URL and CSP Playground will retrieve the CSP from the response headers or a &lt;code&gt;&amp;lt;meta&gt;&lt;/code&gt; tag. Alternatively, you can paste the headers or the policy string directly. Everything stays in the browser, with the exception of URL lookups.&lt;p&gt;Whichever approach you choose, the editor is pre-populated so you can quickly see what is being enforced.&lt;p&gt;If the lookup finds problems (duplicates, deprecated directives, odd formatting), it flags them and suggests fixes.&lt;p&gt;For sites where there is no CSP at all, the tool does not leave a user at in the lurch: it points you to a short guide on why a policy matters and how to start safely, so auditing either an existing policy or a blank slate becomes the first step toward a stronger CSP.&lt;h3 id=&quot;build&quot;&gt;Build&lt;/h3&gt;&lt;p&gt;Whether you are starting from scratch or updating an existing policy, you can work through each CSP directive using a structured form. Add sources without worrying about syntax, generate (placeholder) nonces and hashes for inline scripts and styles, and copy ready-to-use HTML snippets.&lt;p&gt;Every directive links directly to &lt;a href=&quot;https://developer.mozilla.org/&quot;&gt;MDN&lt;/a&gt;, so the documentation is always one click away when you need it.&lt;h3 id=&quot;improve&quot;&gt;Improve&lt;/h3&gt;&lt;p&gt;As you edit, CSP Playground scores your policy in real time and highlights improvements you can make. Recommendations are practical, and show the expected score increase, simply click on the recommendation, and it takes you straight to the relevant directive. You can also see your potential score if you applied every suggestion. This makes it a whole lot easier to focus on the changes that matter most, even if you’ve never written a CSP before.&lt;h3 id=&quot;deploy&quot;&gt;Deploy&lt;/h3&gt;&lt;p&gt;When you are happy with your shiny new Content Security Policy, CSP Playground generates the full header and provides config snippets for web servers and hosting platforms:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/&quot;&gt;Apache&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nginx.org/en/&quot;&gt;Nginx&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://caddyserver.com/&quot;&gt;Caddy&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.litespeedtech.com/products/litespeed-web-server&quot;&gt;LiteSpeed&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.iis.net/&quot;&gt;Microsoft IIS&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.netlify.com/&quot;&gt;Netlify&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://pages.cloudflare.com/&quot;&gt;Cloudflare Pages&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://vercel.com/&quot;&gt;Vercel&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://traefik.io/traefik&quot;&gt;Traefik&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.envoyproxy.io/&quot;&gt;Envoy&lt;/a&gt;&lt;li&gt;more can be added on request, just &lt;a href=&quot;https://github.com/Nooshu/CSP-Playground/issues&quot;&gt;create an issue&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;You can also toggle a setting so that the server setup only attaches the CSP header to HTML responses, cutting down on repeated headers across all static assets, where they aren’t required.&lt;p&gt;Lastly, you can switch to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy-Report-Only&quot;&gt;Report-Only mode&lt;/a&gt; to test changes before enforcement, with links to MDN guidance to help you roll out your new (or updated) CSP safely.&lt;h2 id=&quot;result&quot;&gt;Result&lt;/h2&gt;&lt;p&gt;So, the first test for the tool was the CSP for this very website, after all it’s the reason I built it! As mentioned at the start of the post, the first result of my initial iteration of the tool caught a stray. It must have been live for quite a while, as I haven’t touched the CSP since February 2025. Thankfully, it wasn’t serious, hence why it went unnoticed. The validator picked it up and changed this:&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;Content-Security-Policy: base-uri &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;child-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;connect-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;default-src &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;img-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://v1.indieweb-avatar.11ty.dev/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;font-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;form-action &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://webmention.io&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;frame-ancestors &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;frame-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://player.vimeo.com/ https://www.slideshare.net/ https://www.youtube.com/ https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;manifest-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;media-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;object-src &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;script-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;inline-speculation-rules&#39;&lt;/span&gt; https://ajax.cloudflare.com https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;script-src-elem &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;inline-speculation-rules&#39;&lt;/span&gt; https://ajax.cloudflare.com https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;style-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;unsafe-inline&#39;&lt;/span&gt; https://giscus.app/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;worker-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To this:&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;Content-Security-Policy: base-uri &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;child-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;connect-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;default-src &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;img-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://v1.indieweb-avatar.11ty.dev/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;font-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;form-action &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://webmention.io&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;frame-ancestors &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;frame-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; https://player.vimeo.com/ https://www.slideshare.net/ https://www.youtube.com/ https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;manifest-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;media-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;object-src &lt;span class=&quot;token string&quot;&gt;&#39;none&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;script-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;inline-speculation-rules&#39;&lt;/span&gt; https://ajax.cloudflare.com https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;script-src-elem &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;inline-speculation-rules&#39;&lt;/span&gt; https://ajax.cloudflare.com https://giscus.app/ https://challenges.cloudflare.com/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;style-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;unsafe-inline&#39;&lt;/span&gt; https://giscus.app/&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;worker-src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It took me a little while to spot the difference because the validator simply reported a &quot;semicolon change&quot;. If you are able to notice these small changes straight away, congratulations, you’re more observant than I am!&lt;p&gt;For any readers wondering the only difference is that the trailing semicolon in the second version has been removed. It’s actually entirely optional and has no effect on how the CSP behaves.&lt;p&gt;I admit it’s hardly a groundbreaking discovery, but it was reassuring to see that the validation functionality was doing exactly what it should.&lt;p&gt;The next changes it recommended made more of an impact and improved my CSP score. The initial score was &lt;strong&gt;79%&lt;/strong&gt;. Not bad, but we can do better with only 2 minor changes:&lt;h3 id=&quot;trusted-types-tt&quot;&gt;trusted-types (TT)&lt;/h3&gt;&lt;p&gt;I must admit I’d never heard of this directive until the tool recommended it.&lt;p&gt;It&#39;s only very recently (February) that it has been marked as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API&quot;&gt;Baseline in the MDN documentation&lt;/a&gt; due to broad cross browser support across all modern browsers.&lt;p&gt;It was actually Mozilla who were dragging their feet on this directive, but thankfully changed their stance on it &lt;a href=&quot;https://www.theregister.com/security/2023/12/21/mozilla-decides-trusted-types-is-a-worthy-security-feature/1141670&quot;&gt;back in 2023&lt;/a&gt;. Full support in Firefox was released in version 148 (released 24 February 2026).&lt;p&gt;If you’re curious about this directive, here’s an &lt;a href=&quot;https://en.wiktionary.org/wiki/ELI5&quot;&gt;ELI5&lt;/a&gt; explanation:&lt;ul&gt;&lt;li&gt;Think of an Aeroplane at an airport.&lt;li&gt;Before TT: Anybody could walk straight onto the plane.&lt;li&gt;After TT: Everybody must:&lt;ol&gt;&lt;li&gt;Go through security.&lt;li&gt;Have their passport checked.&lt;li&gt;Receive an approved boarding pass.&lt;/ol&gt;&lt;/ul&gt;&lt;p&gt;Only passengers (or potentially executable content in this analogy) with a valid boarding pass are allowed onto the plane.&lt;p&gt;Trusted Types are the boarding pass.&lt;p&gt;For readers who are older than 5, you can read all about the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API&quot;&gt;Trusted Types API in great depth on MDN&lt;/a&gt;.&lt;h3 id=&quot;upgrade-insecure-requests&quot;&gt;upgrade-insecure-requests&lt;/h3&gt;&lt;p&gt;This directive is pretty self-explanatory given the name. &lt;code&gt;upgrade-insecure-requests&lt;/code&gt; instructs a browser to automatically upgrade HTTP requests to HTTPS before loading resources. Resources that do not support HTTPS will fail to load, although this is &lt;a href=&quot;https://almanac.httparchive.org/en/2025/security#transport-security&quot;&gt;very rare on the modern web&lt;/a&gt;.&lt;p&gt;By simply adding:&lt;p&gt;&lt;code&gt;trusted-types &#39;none&#39;; upgrade-insecure-requests&lt;/code&gt;&lt;p&gt;to my CSP, it bumped the score from &lt;strong&gt;79% up to 87%&lt;/strong&gt;!&lt;p&gt;A respectable gain for what was, in the grand scheme of things, barely any work!&lt;p&gt;Adding &lt;code&gt;upgrade-insecure-requests&lt;/code&gt; improves protection against mixed content by upgrading HTTP resources to HTTPS. Meanwhile, &lt;code&gt;trusted-types &#39;none&#39;&lt;/code&gt; does not actually enforce Trusted Types, but it does make the decision to not use them very explicit.&lt;p&gt;For the moment I have simply disabled the use of Trusted Types in the CSP, but in the future I will likely enable enforcement via the use of:&lt;p&gt;&lt;code&gt;require-trusted-types-for &#39;script&#39;; trusted-types default;&lt;/code&gt;&lt;p&gt;Once enforced this will bring the sites CSP score up to 95%! 🎉&lt;p&gt;&lt;strong&gt;Update 1&lt;/strong&gt;: I tested &lt;code&gt;require-trusted-types-for &#39;script&#39;;&lt;/code&gt; with &lt;code&gt;&#39;trusted-types default;&lt;/code&gt;, but it breaks a few scripts across the site, so have reverted to &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt;. I will update once I have resolved the issues. It just goes to show how much testing you need to do when you change your site&#39;s CSP! Use &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; if in doubt!&lt;p&gt;&lt;strong&gt;Update 2&lt;/strong&gt;: It turns out these errors occur because some of these scripts use legacy code, such as:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// or element.innerHTML = `&amp;lt;p&gt;${text}&amp;lt;/p&gt;`;&lt;/span&gt;
element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;innerHTML &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;p&gt;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;/p&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To properly resolve these issues, you&#39;d need to refactor the scripts to use more standard DOM manipulation methods, like:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; p &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;p&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;p&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The problem comes when you don&#39;t &quot;own&quot; these scripts, e.g. a 3rd-party script. It&#39;s just not practical to update these scripts without the original author(s) being involved, as any new version of the script will overwrite the changes.&lt;p&gt;There is a workaround:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// run this code immediately using an Immediately Invoked Function Expression (IIFE)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// check see if the browser supports the trustedTypes API&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;trustedTypes&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;createPolicy&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Create a Trusted Types policy called &quot;default&quot; policy for the browser to use e.g. `trusted-types default;`&lt;/span&gt;
		trustedTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createPolicy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;default&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// allow all HTML through e.g. I trust every HTML string. (this is bad!)&lt;/span&gt;
			&lt;span class=&quot;token function function-variable&quot;&gt;createHTML&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is critical that this code executes &lt;strong&gt;before&lt;/strong&gt; the browser tries to execute &lt;code&gt;innerHTML&lt;/code&gt;, as without the policy the code will fail. Unfortunately, this code has a major security implication: XSS vulnerabilities are no longer prevented.&lt;p&gt;Given this implication, I&#39;m going to stick to &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt; for the moment, or at least until I look into using &lt;a href=&quot;https://github.com/cure53/DOMPurify&quot;&gt;DOMPurify&lt;/a&gt; to &quot;enable&quot; XSS protection.&lt;p&gt;&lt;strong&gt;Update 3&lt;/strong&gt;: I have tried a solution using ) on my preview environments, and I&#39;m still seeing a host of little issues like missing images in some functionality, so I&#39;ll stick with &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt; for the moment as it&#39;s the only otion that works at the moment. It&#39;s unfortunate that it really isn&#39;t adding much in terms of site security, and it&#39;s only there for the &quot;CSP Score&quot;, but on the positive side it&#39;s been another CSP learning curve for me! So when using the &lt;code&gt;trusted-types&lt;/code&gt; directive, you should definetly expect issues with JavaScript that uses legacy code! It could be a while before anyone gets to roll out this directive without issues!&lt;p&gt;**Update 3: I have tried a solution using &lt;a href=&quot;https://github.com/cure53/DOMPurify&quot;&gt;DOMPurify&lt;/a&gt; on my preview environments, and I&#39;m still seeing a host of little issues like missing images in some functionality. So, I&#39;ll stick with for the moment, as it&#39;s the only option that works at the moment. It&#39;s unfortunate that it really isn&#39;t adding much in terms of site security, and it&#39;s only there for the &quot;CSP Score&quot;, but on the positive side it&#39;s been another CSP learning curve for me! So when using the &lt;code&gt;trusted-types&lt;/code&gt; directive, you should definitely expect issues with JavaScript that uses legacy code! It could be a while before anyone gets to roll out this directive without issues!&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;I promised myself that I wouldn’t make this blog post an epic read this time, so fingers crossed I have kept my promise! If you happen to be interested in my “strategy” and opinion (for what it&#39;s worth) on AI assisted coding off the back of this post, I’ve wrote all about it here in my &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/&quot;&gt;FractalAI: Generating Infinity in the Browser&lt;/a&gt; post from April.&lt;p&gt;Lastly, you can find &lt;a href=&quot;https://csp-playground.dev/&quot;&gt;CSP Playground here&lt;/a&gt;, and the &lt;a href=&quot;https://github.com/Nooshu/CSP-Playground&quot;&gt;GitHub repo for the code here&lt;/a&gt;. &lt;a href=&quot;https://github.com/Nooshu/CSP-Playground/pulls&quot;&gt;PRs&lt;/a&gt; and &lt;a href=&quot;https://github.com/Nooshu/CSP-Playground/issues&quot;&gt;Issues&lt;/a&gt; welcome!&lt;p&gt;I hope you find this little tool useful! I’ve enjoyed building it and learning a little more about the world of CSPs, and Trusted Types.&lt;p&gt;As always, thanks for reading, feedback and comments are welcome. You can &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;contact me here&lt;/a&gt;.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;20/06/26: Initial post published.&lt;li&gt;20/06/26: Reverted &lt;code&gt;require-trusted-types-for &#39;script&#39;;&lt;/code&gt; with &lt;code&gt;trusted-types default;&lt;/code&gt; to &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt; because it broke a number of scripts on the site!&lt;li&gt;20/06/26: Added information about the &lt;code&gt;require-trusted-types-for &#39;script&#39;;&lt;/code&gt; workaround before 3rd-party scripts are updated to use non-legacy DOM methods. Sticking with &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt; for the moment.&lt;li&gt;21/06/26: I tried to resolve issues with &lt;code&gt;require-trusted-types-for &#39;script&#39;;&lt;/code&gt; with &lt;code&gt;trusted-types default;&lt;/code&gt; by using &lt;a href=&quot;https://github.com/cure53/DOMPurify&quot;&gt;DOMPurify&lt;/a&gt;, but there are still other problems that pop up, so still sticking with &lt;code&gt;trusted-types &#39;none&#39;;&lt;/code&gt;. I&#39;ll chalk this one up to a learning exercise, and leave it as is for now! After all, this post was only supposed to be about &lt;a href=&quot;https://csp-playground.dev/&quot;&gt;CSP Playground&lt;/a&gt;, not the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/trusted-types&quot;&gt;Trusted Types directive&lt;/a&gt;.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>FractalAI: Generating Infinity in the Browser</title>
    <link href="https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/" />
    <updated>2026-04-21T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/</id>
    <content type="html">&lt;h2 id=&quot;why-i-built-fractalai&quot;&gt;Why I Built FractalAI&lt;/h2&gt;&lt;p&gt;Although AI has been a concept that has been discussed for the past 80 years (Thanks to Alan Turing, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Artificial_intelligence#History&quot;&gt;many others&lt;/a&gt;), it’s only in the past 5 to 10 years that it has become vastly more powerful. It is currently the hot topic in pretty much every industry on the planet! Software development in particular has already changed drastically, with new tools and AI Models popping up every week. You only have to look at the &lt;a href=&quot;https://survey.stackoverflow.co/2025/ai&quot;&gt;Stack Overflow Developer Survey 2025&lt;/a&gt; to see how common its usage is in Software development:&lt;ul&gt;&lt;li&gt;84% of developers are using or planning to use AI&lt;li&gt;51% of professional developers use it daily&lt;li&gt;61% of professional developers look at it favourably&lt;li&gt;35% of developers visit Stack Overflow due to AI-related issues&lt;/ul&gt;&lt;p&gt;So like it or not, AI is here, and it is here to stay. So to keep up with &quot;the pack” it’s probably a good idea to start learning how to use it, (and more importantly) where &lt;a href=&quot;https://newsletter.pragmaticengineer.com/p/when-ai-writes-almost-all-code-what&quot;&gt;to be cautious&lt;/a&gt;!&lt;h2 id=&quot;the-constraint-100-ai-generated-code&quot;&gt;The Constraint: 100% AI-Generated Code&lt;/h2&gt;&lt;p&gt;To get to grips with AI and its current limits, I decided to set myself a task: build a new hobby project using only AI. All code, all functionality, and even all the design would be generated by me prompting the AI to tweak and generate the frontend code. This is an excellent way to see how AI handles CSS and general usability. For this, I use an AI Tool called &lt;a href=&quot;https://cursor.com/&quot;&gt;Cursor&lt;/a&gt;. There are many tools available, but since subscribing to their pro plan, I’ve seen no real reason to change. Some of the key advantages I see over other AI tools are:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Project-wide context awareness&lt;/strong&gt;: Cursor understands the entire codebase rather than a single file, which leads to more accurate suggestions and safer large-scale changes across interconnected systems.&lt;li&gt;&lt;strong&gt;Multi-file editing and refactoring&lt;/strong&gt;: Cursor can apply consistent updates across multiple files in one action, making large refactors and cross-cutting changes significantly more efficient.&lt;li&gt;&lt;strong&gt;AI-driven workflows&lt;/strong&gt;: Cursor acts as an AI agent that can plan and execute multistep development tasks rather than just offering inline code suggestions.&lt;li&gt;&lt;strong&gt;Context-aware integrated chat&lt;/strong&gt;: Cursor’s built-in chat has full awareness of the repository and can directly modify code, reducing the need for manual context sharing.&lt;li&gt;&lt;strong&gt;Model flexibility&lt;/strong&gt;: Cursor allows developers to switch between different AI models, enabling optimisation for performance, cost, or task-specific quality.&lt;li&gt;&lt;strong&gt;Optimised for complex systems&lt;/strong&gt;: Cursor performs particularly well in large and complex codebases where deeper reasoning and architectural understanding are required.&lt;li&gt;&lt;strong&gt;Faster end-to-end task completion&lt;/strong&gt;: Cursor often reduces overall development time by completing entire tasks in fewer steps, even if individual code suggestions are not the fastest.&lt;li&gt;&lt;strong&gt;AI-native IDE design&lt;/strong&gt;: Cursor is built from the ground up as an AI-first environment, enabling tighter integration and more advanced workflows than plugin-based alternatives.&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;FYI&lt;/strong&gt;: I’m not sponsored by Cursor, I just love how clean and simple it is too pickup and use! There are &lt;a href=&quot;https://www.gartner.com/reviews/market/ai-code-assistants&quot;&gt;many others on the web&lt;/a&gt; that you can choose instead!&lt;h2 id=&quot;what-fractalai-actually-does&quot;&gt;What FractalAI Actually Does&lt;/h2&gt;&lt;p&gt;Well, as you may have guessed already from the name of the blog post, the project I chose to build what a fractal generator 100% built on modern browser technology. The code is all &lt;a href=&quot;https://github.com/Nooshu/FractalAI?tab=License-1-ov-file&quot;&gt;open-source&lt;/a&gt; and free for anyone to clone, modify and use as they wish! The code repository is &lt;a href=&quot;https://github.com/Nooshu/FractalAI&quot;&gt;on GitHub here&lt;/a&gt;.&lt;p&gt;The project currently comes with the following features:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Fractal Selection&lt;/strong&gt;: Choose from 100+ fractal types via dropdown menu.&lt;li&gt;&lt;strong&gt;Adjustable Iterations&lt;/strong&gt;: Control detail level (10 to 400 iterations).&lt;li&gt;&lt;strong&gt;35+ Colour Schemes&lt;/strong&gt;: Classic, Fire, Ocean, Rainbow variants, Monochrome, Forest, Sunset, Purple, Cyan, Gold, Ice, Neon, Cosmic, Aurora, Coral, Autumn, Midnight, Emerald, Rose Gold, Electric, Vintage, Tropical, Galaxy, Lava, Arctic, Sakura, Volcanic, Mint, Sunrise, Steel, Prism, Mystic, Amber, and more.&lt;li&gt;&lt;strong&gt;Zoom and Pan&lt;/strong&gt;: Click and drag to pan, scroll or double-click to zoom.&lt;li&gt;&lt;strong&gt;Real-time Parameter Adjustment&lt;/strong&gt;: Adjust Julia set constants, scales, and other parameters in real-time.&lt;li&gt;&lt;strong&gt;Screenshot Capture&lt;/strong&gt;: Save current view as PNG with EXIF metadata.&lt;li&gt;&lt;strong&gt;FPS Monitoring&lt;/strong&gt;: Real-time frame rate display.&lt;li&gt;&lt;strong&gt;Coordinate Display&lt;/strong&gt;: View and copy exact fractal coordinates.&lt;li&gt;&lt;strong&gt;Presets System&lt;/strong&gt;: Quick-load pre-configured fractal views.&lt;li&gt;&lt;strong&gt;Favourites System&lt;/strong&gt;: Save and manage favourite fractal configurations.&lt;li&gt;&lt;strong&gt;Share &amp; URL State&lt;/strong&gt;: Share fractals via URL with encoded state.&lt;/ul&gt;&lt;p&gt;This was just a copy / paste from the &lt;code&gt;README.md&lt;/code&gt; file in the repository, it actually comes with more features that you can read about like &lt;a href=&quot;https://github.com/Nooshu/FractalAI?tab=readme-ov-file#machine-learning-powered-discovery&quot;&gt;Machine Learning-Powered Discovery&lt;/a&gt;, &lt;a href=&quot;https://github.com/Nooshu/FractalAI?tab=readme-ov-file#performance-optimizations&quot;&gt;Performance Optimizations&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Nooshu/FractalAI?tab=readme-ov-file#advanced-features&quot;&gt;Advanced Features&lt;/a&gt;.&lt;p&gt;I’ve been fascinated with Fractals for many years, I actually dabbled with fractal generation and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API&quot;&gt;Web Workers&lt;/a&gt; back in &lt;a href=&quot;https://nooshu.com/lab/mandelbrot-set-renderer-using-javascript-and-the-canvas-api/&quot;&gt;January 2010&lt;/a&gt;, scarily that’s a whole 16-years ago! That makes me feel very 👴!&lt;p&gt;The reason fractals have always fascinated me is perfectly summed up in this quote from the godfather of fractals &lt;a href=&quot;https://en.wikipedia.org/wiki/Benoit_Mandelbrot&quot;&gt;Benoît Mandelbrot&lt;/a&gt;:&lt;blockquote&gt;&lt;p&gt;Bottomless wonders spring from simple rules, which are repeated without end.&lt;/blockquote&gt;&lt;p&gt;I find it simply astounding that a whole infinite world can be rendered by a computer using such &quot;simple&quot; mathematics. For example, the iconic Mandelbrot fractal is all generated via this neat little formula:&lt;section&gt;&lt;eqn&gt;&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.6389em;vertical-align:-.2083em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:.044em&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-.044em;margin-right:.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mtight mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.2083em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.1111em;vertical-align:-.247em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:.044em&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.8641em&quot;&gt;&lt;span style=&quot;top:-2.453em;margin-left:-.044em;margin-right:.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3.113em;margin-right:.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.247em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2222em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mbin&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2222em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/eqn&gt;&lt;/section&gt;&lt;p&gt;Where:&lt;ul&gt;&lt;li&gt;&lt;eq&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.5806em;vertical-align:-.15em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:.044em&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.3011em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-.044em;margin-right:.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.6444em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;0&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/eq&gt;&lt;li&gt;&lt;eq&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.5782em;vertical-align:-.0391em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;∈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.6889em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathbb&quot;&gt;C&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/eq&gt;(a complex number)&lt;/ul&gt;&lt;p&gt;A point&lt;eq&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;c&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/eq&gt;belongs to the Mandelbrot set if the sequence does not diverge:&lt;section&gt;&lt;eqn&gt;&lt;span class=&quot;katex-display&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:1.45em;vertical-align:-.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mop op-limits&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.6944em&quot;&gt;&lt;span style=&quot;top:-2.4em;margin-left:0&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mtight&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;mtight mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mord mtight&quot;&gt;∞&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;top:-3em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:3em&quot;&gt;&lt;/span&gt;&lt;span&gt;&lt;span class=&quot;mop&quot;&gt;lim&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.7em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.1667em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mord mathnormal&quot; style=&quot;margin-right:.044em&quot;&gt;z&lt;/span&gt;&lt;span class=&quot;msupsub&quot;&gt;&lt;span class=&quot;vlist-t vlist-t2&quot;&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.1514em&quot;&gt;&lt;span style=&quot;top:-2.55em;margin-left:-.044em;margin-right:.05em&quot;&gt;&lt;span class=&quot;pstrut&quot; style=&quot;height:2.7em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mtight reset-size6 size3 sizing&quot;&gt;&lt;span class=&quot;mord mathnormal mtight&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-s&quot;&gt;​&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;vlist-r&quot;&gt;&lt;span class=&quot;vlist&quot; style=&quot;height:.15em&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∣&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;&lt;span class=&quot;mord vbox&quot;&gt;&lt;span class=&quot;thinbox&quot;&gt;&lt;span class=&quot;rlap&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.8889em;vertical-align:-.1944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;inner&quot;&gt;&lt;span class=&quot;mord&quot;&gt;&lt;span class=&quot;mrel&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;fix&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;mspace nobreak&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;→&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord&quot;&gt;∞&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/eqn&gt;&lt;/section&gt;&lt;p&gt;Which generates this absolutely stunning image that you can literally zoom in to forever!&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-300.webp 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-600.webp 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-814.webp 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-1200.webp 1200w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-1250.webp 1250w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The classic shape of the Mandelbrot fractal using my FractalAI generator with the Lava colour scheme.&quot; decoding=&quot;async&quot; height=&quot;781&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-300.jpeg&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-300.jpeg 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-600.jpeg 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-814.jpeg 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-1200.jpeg 1200w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/GZqDDv3k3D-1250.jpeg 1250w&quot; width=&quot;1250&quot;&gt;&lt;/picture&gt;&lt;p&gt;The image above really doesn’t do the details of the Mandelbrot justice! So why not take a look around the &lt;a href=&quot;https://fractals.world/?seed=eyJ0IjoibWFuZGVsYnJvdCIsImMiOiJjb3JhbCIsInoiOjIsImkiOjQwMCwib3giOi0wLjUzNSwib3kiOi0wLjAxNSwieHMiOjEsInlzIjoxfQ&quot;&gt;original setting I used to render it here&lt;/a&gt;.&lt;h2 id=&quot;why-the-browser-is-surprisingly-good-at-rendering-fractals&quot;&gt;Why the Browser Is Surprisingly Good at Rendering Fractals&lt;/h2&gt;&lt;p&gt;What makes the browser surprisingly good at rendering fractals is not just raw capability, but accessibility and reach. A fractal renderer written in the browser can run anywhere instantly with no installation, no setup, and no platform constraints. That is a powerful starting point. Additionaly there&#39;s JavaScript, as the language behind this project, it is embedded in every modern browser, making it one of the most widely available execution environments in the world.&lt;p&gt;This accessibility isn&#39;t theoretical, it shows up in everyday life. Just last month, while walking my 8-year-old son home from primary school, he told me about what he had been learning in &quot;Coding Club&quot; using his &lt;a href=&quot;https://microbit.org/new-microbit/&quot;&gt;BBC micro:bit&lt;/a&gt;. He was genuinely excited when he said:&lt;blockquote&gt;&lt;p&gt;Daddy, I’ve been learning all about something called JavaScript!&lt;/blockquote&gt;&lt;p&gt;That moment really highlighted the scale of JavaScript’s reach. It is not just a professional tool, it is something being introduced at a very young age. When I explained that it forms a large part of my own work, it reinforced how deeply embedded it is in both education and industry.&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-300.heif 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-600.heif 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-800.heif 800w&quot;&gt;&lt;img alt=&quot;The BBC micro:bit kit I purchased for my son contains: BBC micro:bit V2 board, Micro USB cable, Battery pack (holder), and 2× AAA batteries&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-300.webp&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-300.webp 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-600.webp 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/OetDnwH4Om-800.webp 800w&quot; width=&quot;800&quot;&gt;&lt;/picture&gt;&lt;p&gt;It is remarkable to think that a language first created by &lt;a href=&quot;https://en.wikipedia.org/wiki/Brendan_Eich&quot;&gt;Brendan Eich&lt;/a&gt; in just 10-days has evolved into a platform capable of driving complex visualisations like fractal rendering!&lt;p&gt;This, combined with modern browser features such as high-performance &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Engine/JavaScript&quot;&gt;JavaScript engines&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API&quot;&gt;GPU acceleration&lt;/a&gt; through &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt;, and efficient &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API&quot;&gt;canvas APIs&lt;/a&gt;, the browser has become an unexpectedly capable environment for this kind of computationally intensive work.&lt;h2 id=&quot;ai-as-a-design-partner-not-just-a-tool&quot;&gt;AI as a Design Partner, Not Just a Tool&lt;/h2&gt;&lt;p&gt;I’ve found that AI is most powerful when it is treated as a design partner rather than just a tool for generating code. While it is clearly effective at implementation, its real strength lies in collaboration. You can ask it to plan a feature, and it will respond with probing questions that help clarify intent, constraints, and direction. Those questions are not incidental, they are often the key to shaping a better outcome, giving you the opportunity to guide and refine the approach early in a project.&lt;p&gt;Because of the breadth of knowledge it draws on, the interaction feels less like issuing instructions and more like a back and forth design &quot;conversation&quot;. It becomes a space for exploring ideas, testing assumptions, and iterating quickly before committing to an ideal solution.&lt;p&gt;This became particularly valuable in areas where I would not usually feel confident, such as design. Rather than treating that as a limitation, I leaned into AI to generate an initial direction. From there, my role shifted to refinement. Once a baseline design was in place, I could make targeted adjustments to the CSS and HTML, either through follow-up prompts or by working directly with insights gathered from the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Tools_and_setup/What_are_browser_developer_tools&quot;&gt;Browser DevTools&lt;/a&gt;.&lt;p&gt;Although I had originally set myself the constraint of not writing any code, I found that introducing precise, real-world context from DevTools significantly improved the quality of the AI’s output (more on this later in the post). By feeding in specific observations rather than abstract instructions, the responses became more relevant, more predictable, and easier to iterate on.&lt;p&gt;This is where AI moves beyond being a simple tool. It becomes a collaborator that helps shape both the design thinking and the implementation, with you firmly guiding the direction at each step.&lt;h2 id=&quot;the-architecture-without-writing-code-myself&quot;&gt;The Architecture (Without Writing Code Myself)&lt;/h2&gt;&lt;p&gt;This was easily the most engaging part of the process and where the most learning happened. Having the freedom to choose technologies and then observe how AI composes them into a coherent architecture is genuinely insightful, especially when you examine the reasoning behind those decisions.&lt;p&gt;When you are unsure whether a technology is a good fit for a project, you can quickly create a &quot;throwaway&quot; prototype and evaluate it. That shifts the role of AI from a coding assistant to a decision support tool. You are not just generating output, you are generating evidence.&lt;p&gt;Over time, this creates a feedback loop where each experiment improves your judgement. You learn what works, what to avoid, and why.&lt;p&gt;This aligns closely with core Agile principles:&lt;ul&gt;&lt;li&gt;Fail early to surface risk&lt;li&gt;Iterate quickly to explore options&lt;li&gt;Learn continuously to improve decisions&lt;/ul&gt;&lt;p&gt;If you use AI for anything, use it to accelerate learning. Speed only matters when it leads to better decisions.&lt;h2 id=&quot;machine-learning-inside-the-browser&quot;&gt;Machine Learning Inside the Browser&lt;/h2&gt;&lt;p&gt;For many years, I’d wanted to have a play with machine learning (ML), but had never really had the opportunity. With the arrival of browser-based libraries like TensorFlow.js, the idea of running ML directly in the browser suddenly became far more accessible.&lt;p&gt;In this project, I saw an interesting opportunity to apply it to the “random fractal position” feature. The goal was simple on the surface. A user selects a fractal and a colour scheme, presses a button, and gets a random set of x, y, and z values to explore.&lt;p&gt;In practice, it was not that simple.&lt;p&gt;When the AI first implemented this in plain JavaScript, I ran into a fundamental problem. A large proportion of fractal space is effectively empty, which meant most “random” positions resulted in a blank or uninteresting visual. I initially tried constraining the co-ordinates to specific regions, but with over 100 fractals available, this quickly became unmanageable.&lt;p&gt;So I started thinking differently. Instead of hard-coding “good” regions, what if the system could learn what a good fractal view looks like?&lt;p&gt;That is where machine learning came in. The idea was to train a lightweight model to recognise visually interesting outputs, effectively teaching the random feature to make better choices over time. Using AI to help generate and iterate on this approach made the experimentation surprisingly fast.&lt;p&gt;Originally, I explored using &lt;a href=&quot;https://www.tensorflow.org/js&quot;&gt;TensorFlow.js&lt;/a&gt;, but it turned out to be far too heavy for this use case. Even compressed, it added around 450 KB of JavaScript, which is significant given the existing bundle size for all the fractal rendering functionality.&lt;p&gt;Instead, I opted for &lt;a href=&quot;https://caza.la/synaptic&quot;&gt;Synaptic&lt;/a&gt;. It is much smaller at around 45 KB when Brotli compressed, and while it has not seen recent updates, it proved more than capable for what I needed. It also keeps the overall performance profile of the application in check, which was a key consideration.&lt;p&gt;If required, this approach leaves the door open to swap Synaptic out for a more modern ML library in the future. But for now, it demonstrates something powerful. You can run meaningful machine learning directly in the browser, and use it to enhance user experience in ways that would be difficult to achieve with traditional logic alone.&lt;p&gt;As mentioned in the &lt;a href=&quot;https://github.com/Nooshu/FractalAI?tab=readme-ov-file#machine-learning-powered-discovery&quot;&gt;README.md&lt;/a&gt; file in the GitHub repository it is used for the following functionality:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;ML-Based Scoring&lt;/strong&gt;: Uses Synaptic.js neural network to score fractal configurations.&lt;li&gt;&lt;strong&gt;Hybrid Algorithm&lt;/strong&gt;: Combines fast heuristic screening with ML refinement.&lt;li&gt;&lt;strong&gt;Personalised Learning&lt;/strong&gt;: Trains on your favourites to learn your preferences.&lt;li&gt;&lt;strong&gt;&quot;Surprise Me&quot; Feature&lt;/strong&gt;: Discover interesting new fractal zooms automatically.&lt;li&gt;&lt;strong&gt;Background Training&lt;/strong&gt;: Model retrains automatically as you add favourites.&lt;li&gt;&lt;strong&gt;Local Storage&lt;/strong&gt;: ML model and favourites persist in browser storage.&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You will find all of this functionality in the “Full-screen view” of the fractal viewer. The standard UI was starting to feel quite crowded, so that ended up being the most sensible place for it.&lt;h2 id=&quot;what-worked-surprisingly-well&quot;&gt;What Worked Surprisingly Well&lt;/h2&gt;&lt;h3 id=&quot;planning&quot;&gt;Planning&lt;/h3&gt;&lt;p&gt;Planning from the AI was excellent. This may be a Cursor-specific feature, but the follow-up questions it asked about how I wanted to approach a problem were a really nice touch. When you are unsure which direction the AI will take, this gives you useful insights early on.&lt;h3 id=&quot;crafted-prompts&quot;&gt;Crafted Prompts&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Top AI Tip&lt;/strong&gt;: Detailed prompts made a huge difference. The more specific I was, the more reliable the output became. In some cases, I even included images or copied exact code from DevTools just to make sure the AI fully understood the context.&lt;p&gt;I really liked how &lt;a href=&quot;https://bsky.app/profile/tonyalicea.dev&quot;&gt;Anthony Alicea&lt;/a&gt; explains AI in his &lt;a href=&quot;https://dontimitate.dev/courses/ai-assisted-development/&quot;&gt;Understanding AI-Assisted Software Development course&lt;/a&gt;. He visualises the AI “brain” as a cube, where each smaller blue cube represents a piece of knowledge the AI has learned. It’s a simple idea, but it makes how AI works much easier to grasp.&lt;p&gt;A visualisation from his course is shown below:&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-300.webp 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-600.webp 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-814.webp 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-820.webp 820w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;A graphic of a cube filled with blue squares. Each of the squares represents some knowledge that the AI has learned during its training.&quot; decoding=&quot;async&quot; height=&quot;459&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-300.png 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-600.png 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-814.png 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/jsnmyNTWd2-820.png 820w&quot; width=&quot;820&quot;&gt;&lt;/picture&gt;&lt;p&gt;This “knowledge” could include things like:&lt;ul&gt;&lt;li&gt;Documents&lt;li&gt;Images&lt;li&gt;External data&lt;li&gt;Specifications&lt;li&gt;Examples&lt;li&gt;Instructions&lt;/ul&gt;&lt;p&gt;Your job when prompting the AI is to “sail” across this ocean of knowledge and get to a favourable output on the opposite side. To get better results, you need to give the AI waypoints or &quot;lighthouses&quot; to direct the AI through this ocean of knowledge, your prompts are these &quot;lighthouses&quot;. That way, you guide the path it takes, rather than leaving it to figure one out on its own.&lt;p&gt;So rather than saying:&lt;blockquote&gt;&lt;p&gt;Build me a website that renders fractals&lt;/blockquote&gt;&lt;p&gt;Be more specific:&lt;blockquote&gt;&lt;p&gt;Build me a website that renders fractals. Start with a simple Mandelbrot set and use the Canvas API to draw it. I’d also like you to leverage the WebGL API in the visualisation. Make sure the code is well documented and uses modern linting tools like Stylelint and ESLint to keep it maintainable. I also want to use Vite as the local development server…&lt;/blockquote&gt;&lt;p&gt;Now, you might be thinking, that’s a long prompt! It is. But if you want reliable output, you need to give the AI clear instructions to follow. Otherwise, it will just “set sail” across that vast corpus of information and produce something unfocused because it has too much freedom.&lt;p&gt;If there’s one thing I learned from this project, it’s this: spend time crafting detailed prompts. That’s how you steer the ship across the ocean of information and actually reach the result you want.&lt;h3 id=&quot;rules&quot;&gt;Rules&lt;/h3&gt;&lt;p&gt;Another useful thing I learned is that you can give the AI a consistent set of rules to follow across every prompt. In Cursor, these live in the &lt;code&gt;.cursor/rules&lt;/code&gt; directory in your repository.&lt;p&gt;In the FractalAI project, I ended up with four sets of rules:&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Nooshu/FractalAI/blob/main/.cursor/rules/00-core-standards.mdc&quot;&gt;00-core-standards.mdc&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Nooshu/FractalAI/blob/main/.cursor/rules/10-security-dependencies.mdc&quot;&gt;0-security-dependencies.mdc&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Nooshu/FractalAI/blob/main/.cursor/rules/20-performance-frontend.mdc&quot;&gt;20-performance-frontend.mdc&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Nooshu/FractalAI/blob/main/.cursor/rules/30-testing-quality.mdc&quot;&gt;30-testing-quality.mdc&lt;/a&gt;&lt;/ol&gt;&lt;p&gt;The AI refers to these rules for every prompt, giving it consistent context and guidance. You might notice they look AI-generated, and you’d be right. It’s a bit meta, but I asked Cursor to write the rules it would follow in future prompts.&lt;p&gt;The thinking behind this was simple: it is far more likely to understand its own instructions than my rambling ones!&lt;p&gt;I’m not sure if this is specific to Cursor, or if other AIs follow a similar approach. It would be great to see some standardisation here to allow these rules to be portable across multiple AI tools in the future.&lt;p&gt;If this is already happening, I’d love to know. Please feel free to &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt; if you have any information!&lt;h3 id=&quot;writing-tests&quot;&gt;Writing Tests&lt;/h3&gt;&lt;p&gt;If you are not a fan of writing tests, even though you know you should, AI helps a lot. If it can write the code, it can just as easily write the tests too.&lt;p&gt;Where a human developer might get tired of writing tests, AI doesn&#39;t. That makes it much easier to push towards high coverage and keep things consistent.&lt;p&gt;And if you enjoy that warm feeling of seeing a wall of green in your terminal&#39;s test output, this definitely helps. I’m one of those people:&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-300.webp 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-600.webp 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-814.webp 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-1000.webp 1000w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The code coverage report from my terminal for this very blog where every line of code is covered with a corresponding test, so it’s just a black and green image with columns of “100”’s.&quot; decoding=&quot;async&quot; height=&quot;944&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-300.png 300w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-600.png 600w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-814.png 814w, https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/TVF_JBKYTg-1000.png 1000w&quot; width=&quot;1000&quot;&gt;&lt;/picture&gt;&lt;p&gt;Can you imagine how long it would take to get 100% &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_coverage&quot;&gt;coverage&lt;/a&gt; across all files, &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_coverage#Statement_coverage&quot;&gt;Statements&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Code_coverage#Branch_coverage&quot;&gt;Branches&lt;/a&gt;, &lt;a href=&quot;https://web.dev/articles/ta-code-coverage#function_coverage&quot;&gt;Functions&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/articles/ta-code-coverage#line_coverage&quot;&gt;Lines&lt;/a&gt;? That&#39;s a lot of work for one (obsessive) person.&lt;p&gt;Or, a perfect job for AI!&lt;h3 id=&quot;refactoring&quot;&gt;Refactoring&lt;/h3&gt;&lt;p&gt;Where AI really excelled in this project was its ability to understand even the smallest details of the codebase. That makes refactoring fast and almost effortless.&lt;p&gt;For example, I initially used TensorFlow.js, but quickly realised it was too heavy and overly complex for what I needed. So I gave a simple prompt:&lt;blockquote&gt;&lt;p&gt;Could you swap out TensorFlow.js and use Synaptic instead? The page weight is too high and it’s more complex than required.&lt;/blockquote&gt;&lt;p&gt;The important part here is the added context. By explaining why I wanted the change, I was building up the AI’s understanding of the project.&lt;p&gt;From just that, it learns that I care about performance and keeping things lightweight, which leads to better, more aligned outputs as the project progresses.&lt;p&gt;If you combine AI’s ability to refactor quickly with version control like &lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt;, you are onto a winning combination.&lt;p&gt;You can easily &lt;a href=&quot;https://git-scm.com/docs/user-manual#Documentation/user-manual.txt-branch&quot;&gt;branch&lt;/a&gt; and &lt;a href=&quot;https://git-scm.com/docs/user-manual#def_tag&quot;&gt;tag&lt;/a&gt; different versions of your project to experiment with alternative approaches or technologies. If a refactor does not work out, just roll back to a previous commit, or simply scrap the branch / tag.&lt;p&gt;One thing to watch out for if you do this is be wary of your &lt;code&gt;node_modules&lt;/code&gt; folder. Projects tend to get a bit cranky if the dependencies are not installed correctly. And no, committing &lt;code&gt;node_modules&lt;/code&gt; to your repo is not the answer! 🤮&lt;p&gt;An added bonus when using this technique is if you have good &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/#writing-tests&quot;&gt;test coverage&lt;/a&gt;, you can swap out a technology and quickly run your tests to see what has broken. It makes validating a refactor fast and far less risky.&lt;p&gt;This is where AI and testing really come into their own. The AI can make sweeping changes, and your tests act as the safety net to catch anything that breaks.&lt;h3 id=&quot;proof-of-concepts&quot;&gt;Proof of concepts&lt;/h3&gt;&lt;p&gt;At the start of my career in digital marketing, there was a familiar pattern. Most of the budget went on UX and design. By the time it reached the build phase, you would give an estimate and hear, “We don’t have the budget for that.”&lt;p&gt;Classic waterfall. Siloed teams. And not much left for actually building &quot;the thing&quot;.&lt;p&gt;There was even a running joke that what we really needed was a big “build it” button, so the technology part cost next to nothing.&lt;p&gt;When it comes to proof of concepts, or spikes as some call them, you are essentially writing throwaway code. This is where that “build it” button starts to feel real.&lt;p&gt;With a bit of direction on the tech stack and constraints, you can have AI produce a working prototype in hours, not days. Especially when dealing with unfamiliar technologies.&lt;p&gt;What I’m really saying is that AI is great for testing ideas in code. Going back to the Agile principles I mentioned &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/#the-architecture-without-writing-code-myself&quot;&gt;earlier in the post&lt;/a&gt;:&lt;ul&gt;&lt;li&gt;Fail early to surface risk&lt;li&gt;Iterate quickly to explore options&lt;li&gt;Learn continuously to improve decisions&lt;/ul&gt;&lt;p&gt;These all map perfectly to AI-generated proof of concepts.&lt;p&gt;&lt;strong&gt;Word of warning&lt;/strong&gt;: be careful using AI-generated code in live / production without proper validation. Make sure it goes through the usual checks like Pull Requests (PRs), Continuous Integration, and Deployment (CI/CD) gates.&lt;p&gt;If something goes wrong, you can&#39;t blame the AI (well you could try, but I doubt it would go down well!)&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: There should &lt;strong&gt;always&lt;/strong&gt; be a human reviewing AI-generated code, especially when sensitive data is involved.&lt;h3 id=&quot;making-unfamiliar-things-make-sense&quot;&gt;Making unfamiliar things make sense&lt;/h3&gt;&lt;p&gt;I’ve touched on this a little in the previous sections, one of AI’s superpowers is being able to explain complex concepts to the user when required. This is especially true when it comes to software development and technologies a user has no experience of before. AI is basically an incredibly patient coach who is always willing to answer your questions, no mater how small or stupid they may be! When combined with a Code UI like &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt;, you are really working with a powerful workflow. Being able to highlight individual rows and blocks of code and get AI to explain exactly what is happening is a dream come true for anyone learning to code in 2026! I know I wish I had it when I was a junior developer, far to many years ago!&lt;p&gt;Another thing I found really useful is learning through optimisation. You might write a piece of code you understand, then ask the AI:&lt;ul&gt;&lt;li&gt;Are there any edge cases I have missed?&lt;li&gt;How can I make it more maintainable?&lt;li&gt;Does this code follow best practices for the framework?&lt;li&gt;How can I make it more performant?&lt;li&gt;Will this code perform well if the website is scaled up?&lt;li&gt;How can I make it easier to read for other developers?&lt;li&gt;etc…&lt;/ul&gt;&lt;p&gt;These kinds of questions help reinforce your understanding, especially around the less obvious parts of a language.&lt;h3 id=&quot;building-momentum&quot;&gt;Building momentum&lt;/h3&gt;&lt;p&gt;Because I’m so old, I remember when &lt;a href=&quot;https://stackoverflow.com/&quot;&gt;Stack Overflow&lt;/a&gt; first appeared back in late 2008. It was a game changer for web development and beyond. A place to ask questions and get a range of answers, some useful, some less so.&lt;p&gt;I’ve never been one to answer loads of questions, but I do try to go back and share solutions when I find them.&lt;p&gt;The downside is it breaks your flow. One minute you are productive, the next you are stuck searching for answers, sometimes for hours. And if your problem is niche and not already answered, you could be left waiting days for a response.&lt;p&gt;With AI built into your code editor, you have all that knowledge and reasoning right at your fingertips. No need to leave the comfort of your IDE or go searching the web for that one insight that leads to a “Eureka” moment.&lt;p&gt;The result of using AI in this way is simple. Higher productivity, less frustration, and faster learning.&lt;p&gt;A win all round.&lt;h2 id=&quot;where-ai-fell-short-and-needed-steering&quot;&gt;Where AI Fell Short (and Needed Steering)&lt;/h2&gt;&lt;h3 id=&quot;the-definition-of-insanity&quot;&gt;The definition of insanity&lt;/h3&gt;&lt;p&gt;To quote Albert Einstein:&lt;blockquote&gt;&lt;p&gt;Insanity is doing the same thing over and over again and expecting different results.&lt;/blockquote&gt;&lt;p&gt;At times, this is precisely what using AI felt like. It would get stuck on a particular solution and keep coming back to it, no matter how I rephrased the prompt or tried to steer it in a different direction. It was incredibly frustrating.&lt;p&gt;The only reliable fix was to start a new chat, which meant losing all the project context, or switch to a different model in Cursor. I usually run in “auto&quot; mode to balance cost and capability, but switching models also meant losing that built up understanding within the AI prompt.&lt;p&gt;There may be a better way to handle this without resetting everything? Again, if you know, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;.&lt;h3 id=&quot;over-engineering-simple-problems&quot;&gt;Over-engineering simple problems&lt;/h3&gt;&lt;p&gt;If there’s one thing you don’t need to ask AI to do, it’s over engineer a solution. I saw this a lot with CSS. Even simple requirements would take longer than expected and result in far more code than needed.&lt;p&gt;Take something basic like centring a &lt;code&gt;&amp;lt;div&gt;&lt;/code&gt; across all screen sizes.&lt;p&gt;This is a real example from the Benchmark panel in the top right of the FractalAI UI:&lt;h4 id=&quot;ai-output&quot;&gt;AI output&lt;/h4&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--center-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--center-y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--translate-center&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--container-display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--container-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;--container-justify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; relative&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--container-display&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--container-align&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--container-justify&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.container::before&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;inset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;radial-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;circle&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0.05&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;.centered&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; absolute&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--center-y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--center-x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;--translate-center&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;place-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;1rem + 2px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clamp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;200px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 400px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1 / 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;backdrop-filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;blur&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;8px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    0 2px 4px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0.1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    0 8px 16px &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0.15&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@supports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100dvh&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100dvh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 768px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;.centered&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-50% + 2px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-50% + 2px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why use one way to centre a &lt;code&gt;&amp;lt;div&gt;&lt;/code&gt; when you can use three!&lt;ol&gt;&lt;li&gt;Flexbox (&lt;code&gt;align-items&lt;/code&gt; + &lt;code&gt;justify-content&lt;/code&gt;)&lt;li&gt;Absolute positioning (&lt;code&gt;top&lt;/code&gt; / &lt;code&gt;left&lt;/code&gt; + &lt;code&gt;transform&lt;/code&gt;)&lt;li&gt;Grid (&lt;code&gt;place-items&lt;/code&gt;)&lt;/ol&gt;&lt;p&gt;Additionally, it threw in CSS variables that were never reused and &lt;code&gt;@supports&lt;/code&gt; queries where they were not needed.&lt;p&gt;I’d expect a developer with a good grasp of modern CSS to write something more like this:&lt;h4 id=&quot;flexbox-modern&quot;&gt;Flexbox (modern)&lt;/h4&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;align-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;grid-modern&quot;&gt;Grid (modern)&lt;/h4&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;place-items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100vh&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Look how much more maintainable and easier to debug the simpler approach is.&lt;p&gt;This is definitely something to watch for when reviewing or generating AI code.&lt;h3 id=&quot;confidently-wrong-outputs&quot;&gt;Confidently wrong outputs&lt;/h3&gt;&lt;p&gt;The tricky thing with AI is how convincing it sounds. Even when it&#39;s wrong.&lt;p&gt;I did not see this much in this project, but I ran into it when I worked for Government Digital Service (GDS). I asked Google Gemini for an overview of the changes from WCAG 2.1 to WCAG 2.2. The response sounded accurate, at least from my perspective as someone who understands the basics of accessibility without being an expert.&lt;p&gt;But when I shared it with our dedicated accessibility team, it did not hold up. It was full of inaccuracies and false claims.&lt;p&gt;It is a good reminder not to take AI output at face value, especially on topics you are not deeply familiar with.&lt;h3 id=&quot;inconsistent-outputs-between-prompts&quot;&gt;Inconsistent outputs between prompts&lt;/h3&gt;&lt;p&gt;It’s incredible how much difference a single word in a prompt can make to the output. In this example, I asked the AI to write a test for a hypothetical login function in another project I was working on.&lt;h4 id=&quot;prompt-1&quot;&gt;Prompt 1&lt;/h4&gt;&lt;blockquote&gt;&lt;p&gt;“Write a test for a login function”&lt;/blockquote&gt;&lt;p&gt;AI Output:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;login works&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pass&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;prompt-2&quot;&gt;Prompt 2&lt;/h4&gt;&lt;blockquote&gt;&lt;p&gt;“Write a comprehensive test for a login function”&lt;/blockquote&gt;&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;login&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should succeed with correct credentials&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;pass&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should fail with incorrect password&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;user&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;wrong&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;should fail with empty input&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;login&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Just by adding “comprehensive”, the output is vastly different.&lt;p&gt;This might seem like a negative, and it is something to be aware of, but it links back to the &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/#crafted-prompts&quot;&gt;Crafted Prompts&lt;/a&gt; section earlier. Prompt 1 lacks detail, and even Prompt 2 is still pretty vague.&lt;p&gt;Neither is a great example of how to get the best from AI. It just reinforces the need to be clear and specific in what you are asking for.&lt;p&gt;One extra word. An entirely different level of quality.&lt;h3 id=&quot;performance-blind-spots&quot;&gt;Performance blind spots&lt;/h3&gt;&lt;p&gt;For every project, I work on, web performance is a priority. I see it as the foundation of accessibility, regardless of a user’s device, connection, or location.&lt;p&gt;During this project, I noticed that unless you explicitly ask for it, performance is not a priority in AI-generated code. In some cases, fractals would not render at all.&lt;p&gt;As an example, I asked Cursor to find unoptimised code in FractalAI. It highlighted this:&lt;blockquote&gt;&lt;p&gt;For non line based fractals, ProgressiveRenderer increases iterations on each requestAnimationFrame tick and calls fractalModule.render again to recreate the draw command&lt;/blockquote&gt;&lt;p&gt;This is particularly problematic for the Mandelbrot, and other fractals that are &lt;a href=&quot;https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html&quot;&gt;shader&lt;/a&gt; based. Using &lt;a href=&quot;https://regl-project.github.io/&quot;&gt;regl&lt;/a&gt;, which already calls render() on every &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame&quot;&gt;requestAnimationFrame&lt;/a&gt;, this approach creates a new draw command on every frame. That is extremely inefficient for both CPU and memory.&lt;p&gt;After prompting, the issue was resolved. Thanks to my visual render tests using &lt;a href=&quot;https://playwright.dev/&quot;&gt;Playwright&lt;/a&gt;, I could confirm that none of the fractals broke during the optimisation. Another win for visual testing!&lt;p&gt;This is also something I can automate. As mentioned earlier, Cursor’s &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/#rules&quot;&gt;Rules&lt;/a&gt; feature can enforce performance considerations. Writing a rule for this is now high-up on my to-do list.&lt;h3 id=&quot;forgetting-earlier-constraints&quot;&gt;Forgetting earlier constraints&lt;/h3&gt;&lt;p&gt;This is a surprisingly human like trait of AI. As prompt &quot;conversations&quot; go on, it starts to forget earlier constraints. You often have to repeat yourself or restate key requirements it has quietly ignored.&lt;p&gt;Over time, parts of the project context seem to drift, much like people forgetting details as time passes.&lt;p&gt;Going back to the sailing analogy in &lt;a href=&quot;https://nooshu.com/blog/2026/04/21/fractalai-generating-infinity-in-the-browser/#crafted-prompts&quot;&gt;Crafted Prompts&lt;/a&gt;, it is as if the AI loses sight of the waypoint “lighthouses” you set earlier.&lt;h3 id=&quot;elementary-mistakes&quot;&gt;Elementary mistakes&lt;/h3&gt;&lt;p&gt;This happened more often than I expected. For such an advanced tool, the mistakes were surprisingly basic.&lt;p&gt;I saw this when asking the AI to update outdated &lt;code&gt;npm&lt;/code&gt; packages. A simple prompt like:&lt;blockquote&gt;&lt;p&gt;Update all npm packages in this project, and run the coverage tests to ensure nothing has broken.&lt;/blockquote&gt;&lt;p&gt;In Cursor, you can see what it is “thinking”, and it frequently searched for package versions from 2023 or 2024. It was not using the latest information.&lt;p&gt;It was only when I explicitly told it the current year was 2026 did it start checking for up-to-date versions!&lt;p&gt;This is a good reminder to be explicit when you need current information, especially for dependencies, libraries, and documentation.&lt;h2 id=&quot;performance-challenges-in-the-browser&quot;&gt;Performance Challenges in the Browser&lt;/h2&gt;&lt;p&gt;When I first planned this side project, I was quite naïve. My initial idea was to build a 3D fractal renderer in the browser using &lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt;. It didn’t take long to realise this wasn’t going to be viable. Even on an M1 MacBook, the frame rate was so low it was effectively unusable.&lt;p&gt;This is where AI proved its value again. By quickly prototyping the idea, it became obvious in the first iteration that 3D rendering wasn’t going to work. That allowed me to pivot early to a 2D approach using an entirely different WebGL library.&lt;p&gt;Without that early validation, I could easily have spent far longer trying to optimise something that fundamentally isn’t practical right now. There simply isn’t enough available CPU and GPU power in a typical machine to support real-time 3D fractal rendering in the browser at a usable level.&lt;p&gt;The key thing to understand with fractals is the sheer volume of maths involved in rendering every single pixel. Each pixel can require hundreds, sometimes thousands, of iterations before you get a final value.&lt;p&gt;So, even at a relatively modest resolution like a 1810px × 1131px image, that is over 2 million pixels to compute for that single image. Multiply that by hundreds or thousands of calculations per pixel, and you quickly see the scale of the problem.&lt;p&gt;Without highly optimised code, the browser simply does not stand a chance of keeping up. It becomes less about the idea itself and more about the limits of the hardware running it.&lt;p&gt;Looking ahead, it is hard not to think about how this changes with more advanced GPU capabilities. If compute continues to accelerate, especially with emerging technologies, calculations at this scale could eventually take milliseconds rather than seconds.&lt;p&gt;As you zoom deeper into a fractal, the cost per pixel increases. To preserve detail and avoid visual artefacts, the renderer needs to run more iterations for each pixel. Beyond a certain point, the amount of computation required makes real-time interaction impractically slow.&lt;p&gt;There is also a more fundamental limitation around numerical precision. In the browser, JavaScript numbers use &lt;a href=&quot;https://en.wikipedia.org/wiki/Double-precision_floating-point_format&quot;&gt;IEEE-754 64-bit double precision&lt;/a&gt;, which eventually cannot represent the minuscule coordinate differences needed at very high magnifications.&lt;p&gt;You can push beyond this using higher precision techniques, such as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt&quot;&gt;BigInt-based arithmetic&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format&quot;&gt;“double-double” approaches&lt;/a&gt;, but that additional precision comes at a significant cost. Each pixel becomes even more expensive to compute, which further impacts performance.&lt;p&gt;What this all really comes down to is simple. Rendering fractals efficiently in the browser is hard.&lt;p&gt;That being said, it &lt;em&gt;is&lt;/em&gt; one of the quickest ways to max out your CPU and GPU, turn your computer into a heater, and simultaneously watch your electricity bill shoot up!&lt;h2 id=&quot;what-building-this-changed-about-my-view-of-ai&quot;&gt;What Building This Changed About My View of AI&lt;/h2&gt;&lt;p&gt;When I started this project, I was sceptical about how useful AI could be for coding. I had used tools like &lt;a href=&quot;https://chatgpt.com/&quot;&gt;ChatGPT&lt;/a&gt; and &lt;a href=&quot;https://gemini.google.com&quot;&gt;Google Gemini&lt;/a&gt; for research, but never properly explored the coding side.&lt;p&gt;The term “Vibe coding” didn’t help either. If anything, it put me off. After reading &lt;a href=&quot;https://blog.val.town/vibe-code&quot;&gt;Vibe code is legacy code&lt;/a&gt;, the last thing I wanted was to introduce more legacy code into a project. I also find the term itself a bit meaningless, especially after it was &lt;a href=&quot;https://www.bbc.co.uk/news/articles/cpd2y053nleo&quot;&gt;named word of the year in 2025&lt;/a&gt;. It felt like just another buzzword that would disappear as quickly as it arrived.&lt;p&gt;We have seen this pattern before. Technologies get hyped as “the future” and then quietly fade away. Anyone remember &lt;a href=&quot;https://en.wikipedia.org/wiki/Google_Glass&quot;&gt;Google Glass&lt;/a&gt; from 2013??&lt;p&gt;Looking at the defintion of &quot;vibe coding&quot;:&lt;blockquote&gt;&lt;p&gt;Vibe coding is an intuitive, fast paced way of writing code where you prioritise momentum and experimentation over upfront planning, often with the help of tools&lt;/blockquote&gt;&lt;p&gt;I’m pleased to say this project has thoroughly changed my perspective on AI in software development. I still dislike the term “vibe coding”, but the practice behind it is undoubtedly what I found most useful and genuinely exciting while building FractalAI.&lt;p&gt;As mentioned earlier, if you put strong guard rails in place such as mandatory human reviews for all production PRs, along with solid performance and security practices, AI becomes a genuinely valuable tool for learning and rapid prototyping.&lt;p&gt;That said, I openly admit this is an idealistic view. In reality, there will always be individuals, companies, and bad actors willing to use AI in ways that harm the web for their own gain.&lt;p&gt;When it comes to new and exciting technology on the web, it often follows the same familiar pattern. A few people find ways to exploit it for their own benefit, usually at the expense of everyone else.&lt;p&gt;As &lt;a href=&quot;https://en.wikipedia.org/wiki/Paula_Poundstone&quot;&gt;Paula Poundstone&lt;/a&gt; put it best:&lt;blockquote&gt;&lt;p&gt;This is why we can’t have nice things.&lt;/blockquote&gt;&lt;h2 id=&quot;the-future-of-ai-generated-software&quot;&gt;The Future of AI-Generated Software&lt;/h2&gt;&lt;p&gt;If there’s one thing I know about AI-generated software, it’s that it’s not going anywhere. The AI sector is projected to reach &lt;a href=&quot;https://www.grandviewresearch.com/industry-analysis/artificial-intelligence-ai-market&quot;&gt;$539 billion in 2026&lt;/a&gt;, up from $391 billion in 2025. That’s a 38% increase in a single year. Longer term, it’s expected to push into the multi-trillion dollar range.&lt;p&gt;Spending alone tells the story. Global AI investment hit around &lt;a href=&quot;https://www.gartner.com/en/newsroom/press-releases/2025-09-17-gartner-says-worldwide-ai-spending-will-total-1-point-5-trillion-in-2025&quot;&gt;$1.5 trillion in 2025&lt;/a&gt;, and it’s still accelerating. This isn’t hype, it’s momentum.&lt;p&gt;At the same time, the risks are evolving just as quickly. A new Claude model, Mythos, from &lt;a href=&quot;https://www.anthropic.com/&quot;&gt;Anthropic&lt;/a&gt; has &lt;a href=&quot;https://www.bbc.co.uk/news/articles/c2ev24yx4rmo&quot;&gt;reportedly identified a significant number of vulnerabilities across major operating systems&lt;/a&gt;. Some experts believe it shows an unprecedented ability to detect and potentially exploit security weaknesses. Whether that is true or is just scaremongering remains to be seen, but it’s telling that major financial institutions have already been given early access to the model ahead of any public release.&lt;p&gt;As I’ve said, AI is here to stay. The genie is well and truly out of the bottle.&lt;p&gt;My plan is simple. Learn as much as possible over the coming months and years. This is happening whether we like it or not, so it makes far more sense to understand its potential and how it’s being used than to ignore it.&lt;p&gt;If you don’t, you can be sure others in your respective field will be. As this goes far beyond software development. AI’s reach is expanding fast, so buckle up.&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;This project was an experiment in what happens when you step back from writing code and let AI take the lead. &lt;a href=&quot;https://github.com/Nooshu/FractalAI&quot;&gt;FractalAI&lt;/a&gt; is a fully AI-generated fractal renderer running in the browser, built to explore both the potential and the limits of modern AI-assisted development.&lt;p&gt;Along the way, it became clear that AI is far more than just a code generator. When used properly, it acts as a design partner, a rapid prototyping tool, and a way to accelerate learning. But it&#39;s &lt;strong&gt;not perfect&lt;/strong&gt;. It needs direction, context, and clear constraints to produce reliable results.&lt;p&gt;The browser turned out to be a surprisingly powerful platform for this kind of work, capable of rendering complex, infinite visuals while even supporting lightweight machine learning. Combined with AI, it creates a feedback loop where ideas can be tested, refined, and understood far faster than traditional approaches.&lt;p&gt;The biggest takeaway is simple. AI is not replacing developers, it is changing how we work. If you use it well, it becomes a tool for thinking, learning, and making better decisions, not just writing code faster.&lt;p&gt;Well, there we go, yet another blog post where I’ve somehow written far more than intended! If you’ve made it this far, I’m genuinely impressed. Please accept this virtual gold star as a reward: ⭐&lt;p&gt;As always, feedback and comments are very welcome. You can &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;contact me here&lt;/a&gt;.&lt;h2 id=&quot;fractalai-project&quot;&gt;FractalAI project&lt;/h2&gt;&lt;p&gt;Below, you can find the links to the repository and the live site:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Nooshu/FractalAI&quot;&gt;FractalAI on GitHub&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://fractals.world/&quot;&gt;Live site&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;I’m completely open and actively encourage &lt;a href=&quot;https://github.com/Nooshu/FractalAI/pulls&quot;&gt;raising PR&#39;s&lt;/a&gt; and sending me &lt;a href=&quot;https://github.com/Nooshu/FractalAI/issues&quot;&gt;feedback / issues&lt;/a&gt; about the project (with, or without AI!).&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;21/04/26: Initial post published.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Using Cloudflare Workers and reCAPTCHA v3 for a Static Site Contact Form</title>
    <link href="https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/" />
    <updated>2026-03-09T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;I recently wrote a blog post that involved using a &lt;a href=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/&quot;&gt;Cloudflare Worker to serve Brotli 11&lt;/a&gt; compressed HTML rather than the standard uncompressed HTML file. Off the back of this post, I’ve decided to write one about my Cloudflare Worker setup that I use for my &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;Contact Page&lt;/a&gt;. In order to stop the spam, I also integrated &lt;a href=&quot;https://cloud.google.com/security/products/recaptcha&quot;&gt;reCAPTCHA v3&lt;/a&gt; into the form functionality. The form is pretty simple, it asks a visitor for their full name, email address and the message they with to send to me. The Worker then pulls together this information and sends an email to a custom email address that points to my personal email address. This allows me to easily filter messages that come from the site, which I then have the option to reply to if I so wish.&lt;p&gt;It’s worth noting that this post was heavily inspired by &lt;a href=&quot;https://bsky.app/profile/sia.codes&quot;&gt;Sia Karamalegos&lt;/a&gt;’s post from 2024 about &lt;a href=&quot;https://sia.codes/posts/migrating-netlify-to-cloudflare/&quot;&gt;migrating from Netlify to Cloudflare&lt;/a&gt;. I had the absolute please of meeting Sia in Amsterdam when I spoke at &lt;a href=&quot;https://youtu.be/VdBxrZB9V_c?si=dQm4U77NzD9NKOH1&quot;&gt;Performance.now() 2023&lt;/a&gt;, she was MC for my talk and I really appreciated her support when I was (quite frankly) freaking out with nerves before the talk!&lt;p&gt;So why use a serverless endpoint for the contact form? Well, as I’ve mentioned many times before, this blog is a static site built using &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;11ty&lt;/a&gt; and hosted on the Pro plan of &lt;a href=&quot;https://pages.cloudflare.com/&quot;&gt;Cloudflare Pages&lt;/a&gt;. There simply is no backend to point the POST method too, which is required to send the email.&lt;h2 id=&quot;previous-solutions-i-ve-used&quot;&gt;Previous solutions I’ve used&lt;/h2&gt;&lt;p&gt;This isn’t the first time I’ve changed the way the contact form works on this blog. I’ve counted 5 alternatives that I have used over the years! Isn’t Git great for tracking history! I certainly wouldn’t be able to remember them all with my terrible memory! So, if you aren’t on Cloudflare, or you would rather not use a Worker for this, then there’s a list of perfectly viable alternatives below:&lt;h3 id=&quot;netlify-forms&quot;&gt;Netlify Forms&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://docs.netlify.com/manage/forms/setup/&quot;&gt;Netlify Forms&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free+ (depending on the Netlify tier you choose)&lt;/ul&gt;&lt;p&gt;Netlify Forms is a built-in form handling service for sites hosted on Netlify. It allows you to collect and manage form submissions from static websites without building or maintaining a backend. Submissions can be viewed in the Netlify dashboard or forwarded via email or webhook. I still think it’s a real shame that Cloudflare doesn&#39;t offer a form setup that is as easy to integrate and use as Netlify Forms, although, if it did, I wouldn’t need to be writing this blog post!&lt;h3 id=&quot;cloudflare-pages-functions-mailchannels&quot;&gt;Cloudflare Pages Functions + MailChannels&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://developers.cloudflare.com/pages/functions/&quot;&gt;https://developers.cloudflare.com/pages/functions/&lt;/a&gt; &amp; &lt;a href=&quot;https://www.mailchannels.com/&quot;&gt;https://www.mailchannels.com/&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free plan available ($10+ depending on volume-based pricing)&lt;/ul&gt;&lt;p&gt;When I initially migrated from Netlify to Cloudflare for hosting I used this setup, that was until MailChannels sunset its free email sending service for Cloudflare Workers users (me), at that point I started looking for other solutions. As you will see below.&lt;h3 id=&quot;formspree-io&quot;&gt;Formspree.io&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://formspree.io/&quot;&gt;https://formspree.io/&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free tier available ($15+ a month for paid plans)&lt;/ul&gt;&lt;p&gt;Formspree is a hosted form service that lets you collect form submissions without building your own backend server. You simply point your form at Formspree, and it handles sending submissions to your email address or other API endpoints, it comes with built-in spam protection and integrations for various workflows.&lt;h3 id=&quot;formspark-io-previously-submit-form&quot;&gt;Formspark.io (previously Submit Form)&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://formspark.io/&quot;&gt;https://formspark.io/&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free tier available ($9+ a month for paid plans)&lt;/ul&gt;&lt;p&gt;Formspark is a basic hosted form backend that lets you accept and manage form submissions without the need for your own backend server. You simply submit forms to Formspark, and it stores (or forwards) the data, with options for email notifications, webhooks, and spam filtering.&lt;h3 id=&quot;botpoison&quot;&gt;Botpoison&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://botpoison.com/&quot;&gt;https://botpoison.com/&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free plan available ($4+ per month depending on bot verifications required)&lt;/ul&gt;&lt;p&gt;Botpoison is an invisible anti-spam and bot prevention service for web forms that blocks automated submissions without requiring CAPTCHAs or extra steps from users. It works by analysing form interactions and applying proof-of-work challenges and reputation checks to distinguish human traffic from bots. I started using it when I was still receiving contact form spam from my Formspark.io setup.&lt;h3 id=&quot;resend&quot;&gt;Resend&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://resend.com/&quot;&gt;https://resend.com/&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Cost&lt;/strong&gt;: Free plan available ($20+ for paid plans)&lt;/ul&gt;&lt;p&gt;Resend is a developer-focused email delivery service, providing a simple API and Simple Mail Transfer Protocol (SMTP) interface for sending transactional and broadcast emails from your applications or website. It handles features like deliverability, bounce tracking, suppression lists, and email analytics, so a developer doesn&#39;t need to manage email infrastructure themselves and can be integrated with many platforms. Resend is currently part of my email workflow, as I will describe later in the post.&lt;h2 id=&quot;overview-of-the-setup&quot;&gt;Overview of the setup&lt;/h2&gt;&lt;p&gt;My current setup is: &lt;strong&gt;Cloudflare Pages Functions&lt;/strong&gt; (the Worker) + &lt;strong&gt;Google reCAPTCHA v3&lt;/strong&gt; + &lt;strong&gt;Resend&lt;/strong&gt; for email.&lt;p&gt;A high-level view of this workflow is:&lt;ol&gt;&lt;li&gt;User populates the contact form with their details and message&lt;li&gt;reCAPTCHA v3 runs and adds a short-lived verification token&lt;li&gt;The form POSTs to &lt;code&gt;/api/contact&lt;/code&gt; (via the Pages Function, that sits in &lt;code&gt;functions/api/contact.js&lt;/code&gt; in the root of my repository&lt;li&gt;The Pages Function, then:&lt;ul&gt;&lt;li&gt;verifies the token with Google&lt;li&gt;validates the user inputs&lt;li&gt;sends the validated inputs via email using &lt;a href=&quot;https://resend.com/&quot;&gt;Resend&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;Redirects the user to the “Thank You” page, confirming that the message has been sent.&lt;/ol&gt;&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-300.webp 300w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-600.webp 600w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-814.webp 814w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-1200.webp 1200w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;diagram of the workflow I described in the list above&quot; decoding=&quot;async&quot; height=&quot;655&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-300.jpeg&quot; srcset=&quot;https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-300.jpeg 300w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-600.jpeg 600w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-814.jpeg 814w, https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/22H4uljaxL-1200.jpeg 1200w&quot; width=&quot;1200&quot;&gt;&lt;/picture&gt;&lt;h2 id=&quot;google-recaptcha-v3&quot;&gt;Google reCAPTCHA v3&lt;/h2&gt;&lt;p&gt;I have to say, out of all the things written in this post, researching about how exactly Google reCAPTCHA v3 works, was probably the most fascinating! I really love the fact that it is all invisible to the user, and it is based on a complex (but logical) scoring mechanism. There’s no need for a really frustrating visual CAPTCHA’s that don’t prove you are human, “they prove that you are American!” (I can’t take credit for this quote, that I believe goes to &lt;a href=&quot;https://edent.tel/&quot;&gt;Terence Eden&lt;/a&gt;, or at least that&#39;s who I remember reading it from!)&lt;p&gt;So how exactly does reCAPTCHA v3 decide if you are a human? Well, it turns out it analyses a user&#39;s behavioural and contextual signals rather than setting a user a visual challenge, and based on those signals it creates a risk score. The biggest signal comes from how the user behaves on the page.&lt;h3 id=&quot;behavioural-analysis-of-a-users-session&quot;&gt;Behavioural analysis of a user&#39;s session&lt;/h3&gt;&lt;p&gt;reCAPTCHA observes a number of patterns and metrics from a user&#39;s interaction with the page, these include:&lt;ul&gt;&lt;li&gt;Mouse movement patterns&lt;li&gt;Typing speed and rhythm&lt;li&gt;Scroll behaviour&lt;li&gt;Click timing and placement&lt;li&gt;Focus changes between fields or tabs&lt;/ul&gt;&lt;p&gt;Us humans are a little messy when using a computer. Our mouse movements curve and wander slightly, we pause without thinking, and our typing comes with the occasional hesitation or uneven rhythm.&lt;p&gt;Skynet, on the other hand, tends to be a little too perfect. Its mouse paths are often straight, timing is predictable, and its interactions can happen far faster than any real person would manage. Those overly neat patterns are often a strong indicator that automation is at work.&lt;h3 id=&quot;browser-and-device-fingerprinting&quot;&gt;Browser and device fingerprinting&lt;/h3&gt;&lt;p&gt;Now, when I think of Browser fingerprinting I often associate it with advertising and companies tracking users across the internet. In all fairness, this is Google we are talking about, so it&#39;s very likely that they actually do this! &lt;code&gt;:sad_face:&lt;/code&gt;. Browser and device fingerprinting is the way the reCAPTCHA script also examines the environment and device information of where the request is coming from.&lt;p&gt;Typical signals include:&lt;ul&gt;&lt;li&gt;Browser version and capabilities&lt;li&gt;Installed fonts and plugins&lt;li&gt;Screen resolution and device properties&lt;li&gt;WebGL and Canvas fingerprint signals&lt;li&gt;Cookie availability&lt;li&gt;JavaScript execution behaviour&lt;li&gt;Timezone of client&lt;li&gt;Language settings&lt;/ul&gt;&lt;p&gt;You’d be amazed at how accurate these fingerprints can be. Each piece of information adds a little more entropy, meaning it becomes easier to distinguish one device from another. They are so accurate in fact I know many financial sectors or government departments use them for fraud detection.&lt;p&gt;You really don’t need to look very far to find libraries like &lt;a href=&quot;https://fingerprint.com/github/&quot;&gt;Fingerprint&lt;/a&gt; that do this all for you! Look at all the information it captured about you just by &lt;a href=&quot;https://demo.fingerprint.com/playground&quot;&gt;clicking this link&lt;/a&gt;! And you thought cookie tracking was bad!&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: There are many other fingerprinting libraries available, I’m not specifically targeting &lt;a href=&quot;https://fingerprint.com/github/&quot;&gt;Fingerprint&lt;/a&gt;.&lt;p&gt;So while I disagree with browser and device fingerprinting from a privacy and tracking perspective, it does have its uses when it comes to proving that a “user” is a human and not a bot. In particular, bots often run in headless browsers or minimal environments, which look very different from real user devices.&lt;h3 id=&quot;network-and-reputation-signals&quot;&gt;Network and reputation signals&lt;/h3&gt;&lt;p&gt;Due to Google’s global network, it can also delve into global reputation data. It captures:&lt;ul&gt;&lt;li&gt;IP reputation and history&lt;li&gt;Known bot networks or proxy usage&lt;li&gt;Data centre IP ranges&lt;li&gt;Previous abuse patterns linked to the IP or session&lt;/ul&gt;&lt;p&gt;So much traffic flows through Googles network globally, it can detect suspicious network traffic surprisingly easily.&lt;h3 id=&quot;action-context&quot;&gt;Action Context&lt;/h3&gt;&lt;p&gt;Google can also score the predicted actions a user is going to make. With reCAPTCHA v3, developers call the library with an action name such as:&lt;pre&gt;&lt;code&gt;login
checkout
signup
comment
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;From its countless trillions of observations over the years, Google already knows the typical login behaviour for a human (it takes approximately 3–10 seconds). Whereas, a bot can submit hundreds of logins per minute, which is a huge red flag for the use of automation.&lt;h3 id=&quot;scoring-using-machine-learning&quot;&gt;Scoring using Machine learning&lt;/h3&gt;&lt;p&gt;So what does Google do with all these various signals it captures?&lt;p&gt;It feeds them into a machine learning algorithm for scoring, which will decide if whatever is interacting is a human or a bot.&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Score&lt;th&gt;Meaning&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0.9 to 1.0&lt;td&gt;Very likely human&lt;tr&gt;&lt;td&gt;0.5&lt;td&gt;Uncertain&lt;tr&gt;&lt;td&gt;0.0 to 0.3&lt;td&gt;Very likely bot&lt;/table&gt;&lt;p&gt;At this point, it is then up to &lt;strong&gt;your&lt;/strong&gt; backend to decide what to do with the score it gets back. For example:&lt;pre&gt;&lt;code&gt;0.8+  allow request
0.4–0.8 require MFA
&amp;lt;0.4 block or challenge
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In my case, the Cloudflare Worker is my simple serverless backend, and it contains the following code to decide if the form submit is from a human or a bot:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Verify score (reCAPTCHA v3 returns scores between 0.0 and 1.0)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Lower scores indicate bot-like behavior. 0.5 is a common threshold, but 0.3 allows more legitimate traffic&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;number&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CAPTCHA verification failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Score too low: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The code above is saying to reject any score that returns from Google that is under 0.3 (Very likely bot), You could of course easily change this threshold if you found it was being too aggressive with the filtering.&lt;h3 id=&quot;technical-setup&quot;&gt;Technical setup&lt;/h3&gt;&lt;p&gt;To use reCAPTCHA v3, you must first register your site in the &lt;a href=&quot;https://cloud.google.com/security/products/recaptcha&quot;&gt;Google reCAPTCHA admin console&lt;/a&gt; to obtain two keys.&lt;p&gt;The “site key” is used on the client-side and allows the browser to request a reCAPTCHA verification token for when a user performs an action. The “secret key” must remain server-side and is used to verify that token with Google.&lt;p&gt;This means storing the secret securely as an environment variable, for my setup this is done via the Cloudflare Pages build dashboard. The Cloudflare Worker then reads it from the environment variables during the build process.&lt;p&gt;Once all configured, you can also use the Google reCAPTCHA admin console to:&lt;ul&gt;&lt;li&gt;restrict the key to specific hostnames&lt;li&gt;configure score thresholds to control how strict the bot detection should be&lt;/ul&gt;&lt;h2 id=&quot;the-contact-form-html-and-client-side&quot;&gt;The contact form (HTML and client-side)&lt;/h2&gt;&lt;p&gt;The HTML used for the form is all pretty standard, just remember to make it as accessible as possible! Semantic markup is the key to achieving this! If you are unsure how to do this, there’s an excellent post called &lt;a href=&quot;https://webaim.org/techniques/forms/&quot;&gt;Creating Accessible Forms on the WebAIM.org&lt;/a&gt; website. The HTML for my contact form looks like this:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot; data-gist=&quot;882a6cc0521479659df63e5f442df817&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--
  Contact form: POSTs to the Cloudflare Pages Function at /api/contact.
  Uses novalidate so we can run custom client-side validation and show
  accessible error messages before and after submit.
--&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;fs-frm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/api/contact&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;POST&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-labelledby&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;page-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;novalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--
	  Error summary: shown when validation fails (client or server).
	  role=&quot;alert&quot; announces to screen readers when content appears.
	  tabindex=&quot;-1&quot; allows focus to be moved here after submit for accessibility.
	  hidden by default; JS removes the attribute to reveal and populate it.
	--&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-error-summary&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-error-summary&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alert&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;tabindex&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--
	  Live region for status updates (e.g. &quot;Sending…&quot;, success message).
	  role=&quot;status&quot; is polite; screen readers announce changes without interrupting.
	--&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-status&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-status&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--
	  Honeypot (spam trap): invisible to users, attractive to bots.
	  - Wrapped in a div with a neutral class (e.g. &quot;abc&quot;) so bots don’t skip it.
	  - aria-hidden=&quot;true&quot; keeps it out of the accessibility tree.
	  - tabindex=&quot;-1&quot; and autocomplete=&quot;off&quot; reduce chance a human touches it.
	  - Obscure name so bots don’t recognise it as a honeypot; server rejects if set.
	--&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;abc&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-hidden&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_its-a-trap!&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;tabindex&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;-1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;off&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Server/worker uses these hidden fields for redirect and email behaviour --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_redirect&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/contact/thanks/&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_append&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;_email.subject&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;New Message from Nooshu.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Legend is required for fieldset; visually-hidden keeps layout clean --&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;legend&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;visually-hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Contact form&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;legend&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Full Name:&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;name-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Email:&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autocomplete&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hello@example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;email-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-group&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;required&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Message:&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;textarea&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rows&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;7&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;textarea&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;message-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form-error&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;fieldset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--
	  reCAPTCHA v3 token: client-side JS runs grecaptcha.execute() and sets
	  this value before submit. Server verifies the token with Google before
	  sending the email.
	--&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;hidden&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;g-recaptcha-response&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Send Message&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with &lt;a href=&quot;https://gist.github.com/Nooshu/882a6cc0521479659df63e5f442df817&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fewer comments is available in this gist on GitHub&lt;/a&gt;.&lt;p&gt;The contact form I use above has the following features:&lt;h3 id=&quot;honeypot-and-recaptcha-to-block-spam&quot;&gt;Honeypot and reCAPTCHA to block spam&lt;/h3&gt;&lt;p&gt;I’ve already detailed how Google reCAPTCHA works, so I won’t cover it again here, but a &lt;a href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;Honeypot&lt;/a&gt; is a simple first layer of protection. The basic theory behind a Honeypot is a simple form field hidden from real users but still present in the page markup. Bots that scan the source code often fill in every field they find, including this hidden one. If that field is completed, it is a strong signal that the submission came from a bot, not a human because it isn’t seen in the User Interface (UI), thus allowing the request to be rejected immediately.&lt;h3 id=&quot;client-and-server-validation-to-ensure-safe-input&quot;&gt;Client and server validation to ensure safe input&lt;/h3&gt;&lt;p&gt;Form validation is handled in two layers:&lt;ol&gt;&lt;li&gt;On the client side, the form uses &lt;code&gt;novalidate&lt;/code&gt; with custom JavaScript to check required fields and validate the email format for the name, email, and message inputs.&lt;li&gt;On the server-side, the API performs its own validation by checking that required fields are present, enforcing max length limits for each field (name: 200 characters, email: 320 characters, message: 5000 characters), and confirming that a valid reCAPTCHA token is included with the submission.&lt;/ol&gt;&lt;h3 id=&quot;accessible-error-handling-and-focus-management&quot;&gt;Accessible error handling and focus management&lt;/h3&gt;&lt;p&gt;Accessibility is built into the form by clearly associating it with the page title using &lt;code&gt;aria-labelledby&lt;/code&gt;, providing an error summary and status regions with &lt;code&gt;role=“alert&quot;&lt;/code&gt; plus &lt;code&gt;role=“status&quot;&lt;/code&gt;, also marking individual fields with &lt;code&gt;aria-invalid&lt;/code&gt; and &lt;code&gt;aria-describedby&lt;/code&gt; so errors are properly announced.&lt;p&gt;Inline error messages use matching IDs for assistive technologies, required fields are clearly indicated with a &lt;code&gt;*&lt;/code&gt;, and a visually hidden legend describes the &lt;code&gt;&amp;lt;fieldset&gt;&lt;/code&gt;. When errors occur, the browser focus moves to the summary using &lt;code&gt;tabindex=&quot;-1”&lt;/code&gt; so it can be announced immediately by the screen reader.&lt;h3 id=&quot;ux-updates-to-keep-the-user-informed-during-submission&quot;&gt;UX updates to keep the user informed during submission&lt;/h3&gt;&lt;p&gt;The form includes the &lt;code&gt;autocomplete&lt;/code&gt; attribute for the name and email fields, allowing users browser the option to suggest information already stored in the browser and pre-fill it for them. Additionally, there&#39;s a simple placeholder for the email input, letting a user know what input format is expected.&lt;p&gt;When the form is submitted, the submit button is temporarily disabled and its label changes to “Sending…” while a status message informs the user that their message is being sent. After the submission is successful, the user is then redirected to the &lt;code&gt;/contact/thanks/&lt;/code&gt; page, as seen in the &lt;code&gt;_redirect&lt;/code&gt; input field, the API then returns a 303 redirect to complete the process.&lt;h3 id=&quot;secure-server-side-processing&quot;&gt;Secure server side processing&lt;/h3&gt;&lt;p&gt;The form submits a POST request to &lt;code&gt;/api/contact&lt;/code&gt;, handled by a Cloudflare function. Hidden fields such as &lt;code&gt;_redirect&lt;/code&gt;, &lt;code&gt;_append&lt;/code&gt;, and &lt;code&gt;_email.subject&lt;/code&gt; control what happens on submit.&lt;p&gt;On the server, the submission is sent as an email in both HTML and plain text formats before redirecting the user on success. The endpoint only accepts &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype&quot;&gt;form-encoded&lt;/a&gt; or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods/POST#multipart_form_submission&quot;&gt;multipart&lt;/a&gt; content types. It also verifies the reCAPTCHA token using the &lt;a href=&quot;https://developers.cloudflare.com/fundamentals/reference/http-headers/#cf-connecting-ip&quot;&gt;CF-Connecting-IP header&lt;/a&gt; when available, and validates required fields and length limits, it then &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Safely_inserting_external_content_into_a_page&quot;&gt;escapes HTML in the email body&lt;/a&gt; to ensure the content is safe.&lt;p&gt;I’d like to think this form is pretty accessible and simple to use, but if you disagree, please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;. How very &lt;a href=&quot;https://www.dictionary.com/browse/meta&quot;&gt;meta&lt;/a&gt;! It’s always fun to learn something new!&lt;h2 id=&quot;simplified-csp&quot;&gt;Simplified CSP&lt;/h2&gt;&lt;p&gt;The great thing about not having to use a third-party form service for sending contact form emails (besides the slight reduction in cost), is that it simplifies my &lt;code&gt;Content-Security-Policy&lt;/code&gt;. This is because I’m no longer having to establish a connection to an additional third-party. The Cloudflare Worker runs on &lt;strong&gt;my instance&lt;/strong&gt; of Cloudflare pages, so it is embedded in the build. And thankfully there’s no client-side interaction when using &lt;a href=&quot;https://resend.com/&quot;&gt;Resend&lt;/a&gt;, as this all happens via the Cloudflare Worker, no modifications to the CSP required! This may not seem like a big thing, but the less you can rely on a third parties the better! There are numerous examples from the history of the web, where the failure of a third party has had a massive impact on the web:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.fastly.com/blog/summary-of-june-8-outage&quot;&gt;Fastly outage on 8 June 2021&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.theguardian.com/technology/2016/oct/26/ddos-attack-dyn-mirai-botnet&quot;&gt;Dyn DNS outage (2016)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://www.theguardian.com/technology/2021/oct/05/facebook-outage-what-went-wrong-and-why-did-it-take-so-long-to-fix&quot;&gt;Facebook BGP outage (2021)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Category:2010s_internet_outages&quot;&gt;Wikipedia Category on Internet outages&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;An interesting point about the Wikipedia link above is you can see how prominent these outages have become over the decades, and also the fact that the placeholder category pages for the 2030s, 2040s, 2050s, and 2060s are already listed! Oh well, those outages are for my grandkids to worry about! 👴🏼&lt;h2 id=&quot;the-cloudflare-worker-pages-function&quot;&gt;The Cloudflare Worker (Pages Function)&lt;/h2&gt;&lt;p&gt;So let’s get onto the final piece of the puzzle: the Cloudflare Worker code. This file sits in the root of my blog under &lt;code&gt;functions/api/contact.js&lt;/code&gt;, this translates to the &lt;code&gt;action=&quot;/api/contact”&lt;/code&gt; in the &lt;a href=&quot;https://nooshu.com/blog/2026/03/09/using-cloudflare-workers-and-recaptcha-v3-for-a-static-site-contact-form/#the-contact-form-html-and-client-side&quot;&gt;form code above&lt;/a&gt;.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;0e1cb510f533e8ae9247f84bf9c724b3&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Import a small helper that escapes HTML characters.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This prevents user-supplied content (like the message body)&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// from breaking HTML or injecting markup when we render it in the email.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; escapeHtml &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../../_helpers/escape-html.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Cloudflare Pages Functions export HTTP handlers as named exports.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// `onRequestPost` will be invoked for POST requests to this function&#39;s route.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Signature:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - `request`: the incoming HTTP Request object&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - `env`: environment bindings (secrets, KV, etc.), configured in Cloudflare&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function function-variable&quot;&gt;onRequestPost&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 1. Accept only standard HTML form submissions&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We support:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - application/x-www-form-urlencoded  (classic HTML &amp;lt;form method=&quot;post&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - multipart/form-data                (forms that include files)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Anything else (JSON, etc.) is rejected with 415 Unsupported Media Type.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; contentType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;contentType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;application/x-www-form-urlencoded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;contentType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;multipart/form-data&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unsupported content type&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;415&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Parse the incoming form body. Cloudflare&#39;s Request object&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// exposes a `.formData()` helper which returns a FormData instance.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 2. Honeypot anti-spam field&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// A &quot;honeypot&quot; is an extra hidden field that humans never fill in,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// but bots often do. If this field is present and non-empty,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// we treat it as spam and quietly redirect away.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;_its-a-trap!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Redirect back to the homepage. We build the URL from the current request&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// so it works correctly across environments (preview, production, etc.).&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hpUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hpUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;303&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 3. Extract and normalize form fields&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We pull out the core fields we care about:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - name&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - email&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - message&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - _redirect (optional; where to send the user after success)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Every value is coerced to string and trimmed to avoid&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// issues with null/undefined and accidental leading/trailing spaces.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; redirectUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;_redirect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/contact/thanks/&quot;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Basic required-field validation. If any are empty,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// respond with a 400 Bad Request and a JSON error.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Missing required fields&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 4. Length limits (simple validation / abuse mitigation)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// These caps are deliberately generous but help avoid:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - unrealistic payloads&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - abuse where the form is used as a data pipe&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;320&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Invalid field lengths&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 5. CAPTCHA verification (reCAPTCHA v3 or hCaptcha-compatible)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The form is expected to include a CAPTCHA token:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - reCAPTCHA v3: `g-recaptcha-response`&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - (optionally) hCaptcha-style: `h-captcha-response`&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If no token is present, we immediately reject the submission.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; recaptchaToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
			form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;g-recaptcha-response&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;h-captcha-response&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;recaptchaToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reCAPTCHA token is required&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Select the secret key from Cloudflare environment variables.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// This must be configured in the project/environment:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - RECAPTCHA_SECRET_KEY or&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - RECAPTCHA_SECRET&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; secret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RECAPTCHA_SECRET_KEY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RECAPTCHA_SECRET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// If the secret is missing, that&#39;s a server misconfiguration,&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// so we return a 500 Internal Server Error.&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
						&lt;span class=&quot;token string&quot;&gt;&quot;Server not configured for CAPTCHA (missing RECAPTCHA_SECRET_KEY/RECAPTCHA_SECRET)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// For better fraud detection, we also send the user&#39;s IP to reCAPTCHA.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Cloudflare exposes client IP via:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - CF-Connecting-IP header&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - request.cf.clientAddress&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; remoteIp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
			request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CF-Connecting-IP&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
			request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cf&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;clientAddress &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Build the request body for the reCAPTCHA verification endpoint.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// It expects application/x-www-form-urlencoded payload.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyParams &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URLSearchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			secret&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; recaptchaToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remoteIp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			verifyParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;remoteip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; remoteIp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// POST the verification request to Google&#39;s reCAPTCHA API.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyResp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token string&quot;&gt;&quot;https://www.google.com/recaptcha/api/siteverify&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/x-www-form-urlencoded&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; verifyParams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// If Google&#39;s endpoint itself is failing (network issue or 5xx),&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// treat this as a bad gateway (502) and surface a generic error.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;verifyResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Failed to verify reCAPTCHA&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;502&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Parse the JSON response from reCAPTCHA.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Example structure:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// {&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//   success: true/false,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//   score: 0.0-1.0,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//   hostname: &quot;example.com&quot;,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//   &quot;error-codes&quot;: [...]&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyJson &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; verifyResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 5a. Hard failure: verification not successful at all&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We return a 400 with details about why it failed where possible.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;success&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorCodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;error-codes&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CAPTCHA verification failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;
						errorCodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; errorCodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;, &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Unknown error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 5b. Soft failure: low reCAPTCHA v3 score&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// reCAPTCHA v3 uses scores instead of explicit &quot;I&#39;m not a robot&quot; prompts.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Lower values are more suspicious. Here we reject anything below 0.3.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// This threshold is a trade-off between blocking bots and not annoying users.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;number&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;CAPTCHA verification failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Score too low: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;score&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 5c. Optional hostname verification&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The reCAPTCHA response includes a `hostname` field.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We check that the hostname reCAPTCHA saw matches the hostname of our request.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// This helps prevent token reuse on other domains.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requestHost &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt;
			verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; requestHost &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;requestHost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// We allow subdomains (e.g. www.example.com vs example.com),&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// but log any unexpected mismatch to server logs for later inspection.&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// This log will show up in Cloudflare function logs.&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;reCAPTCHA hostname mismatch: expected &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;requestHost&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, got &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 6. Prepare email content for Resend&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// At this point, the submission has passed:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - basic validation&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - anti-spam honeypot&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - CAPTCHA checks&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Now we build an email to send to the site owner via Resend.&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Fixed &quot;from&quot; address used with Resend.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// This should be a verified sender domain for your Resend account.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fromEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;contact@example.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Allow the subject to be overridden via a hidden form field `_email.subject`,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// otherwise fall back to a sensible default.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
			form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;_email.subject&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;New Message from Nooshu.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Plain-text body, which is useful for mail clients that don&#39;t render HTML, note the use of JavaScript Template literals.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textBody &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;New contact form submission on nooshu.com

Name: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
Email: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;email&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;

Message:
&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;message&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// HTML body with simple markup for better readability.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We escape all user-supplied fields to avoid injecting HTML or scripts.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlBody &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;h2&gt;New contact form submission on nooshu.com&amp;lt;/h2&gt;&amp;lt;p&gt;&amp;lt;strong&gt;Name:&amp;lt;/strong&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;br/&gt;&amp;lt;strong&gt;Email:&amp;lt;/strong&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;&amp;lt;p&gt;&amp;lt;strong&gt;Message:&amp;lt;/strong&gt;&amp;lt;/p&gt;&amp;lt;pre style=&quot;white-space:pre-wrap&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/pre&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Pull the Resend API key from Cloudflare environment variables.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// This must be configured as a secret in the Pages project.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;apiKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Server not configured for email (missing RESEND_API_KEY)&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 7. Call Resend&#39;s email API&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We issue a POST request to Resend&#39;s /emails endpoint with:&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - from: sender (must be a verified domain)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - to: recipient(s) (in this case, the site inbox)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - reply_to: the visitor&#39;s email address, so &quot;Reply&quot; in your mail client goes to them&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// - subject, text, html: the message content&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resendResp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.resend.com/emails&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Bearer &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;apiKey&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Nooshu Contact &amp;lt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;fromEmail&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email@example.com&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;reply_to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				subject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; textBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; htmlBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// If Resend returns a non-2xx status, we surface a 502 to the client&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// and include the raw error body for easier debugging.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;resendResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; resendResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Email send failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;502&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 8. Final redirect on success&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If everything above succeeds, we redirect the user to a &quot;thank you&quot;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// page (configurable via the `_redirect` field).&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// We resolve `redirectUrl` relative to the current request URL&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// to avoid hard-coding the origin.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalRedirect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redirectUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// 303 See Other is the canonical way to redirect after a POST,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// telling the browser to perform a GET to the new URL.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalRedirect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;303&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 9. Global error handler&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If anything unexpected blows up (network issues, runtime errors, etc.),&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// we catch it here and return a generic 500 JSON response.&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The error is stringified so it&#39;s at least inspectable in logs / responses,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// but in a real-world scenario you might want to avoid leaking details&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// to the client and instead only log them server-side.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Server error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&quot;content-type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with &lt;a href=&quot;https://gist.github.com/Nooshu/0e1cb510f533e8ae9247f84bf9c724b3&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fewer comments is available in this gist on GitHub&lt;/a&gt;.&lt;h2 id=&quot;using-cloudflare-turnstile&quot;&gt;Using Cloudflare Turnstile&lt;/h2&gt;&lt;p&gt;After I posted the blog post on &lt;a href=&quot;https://bsky.app/profile/therealnooshu.bsky.social/post/3mgowaxsask2d&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://hachyderm.io/@TheRealNooshu/116203868348768808&quot;&gt;Mastodon&lt;/a&gt;, and &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:activity:7437049173461438464/&quot;&gt;LinkedIn&lt;/a&gt;, I had a couple of replies back asking me why I hadn&#39;t used Cloudflare&#39;s CAPTCHA integration rather than Google&#39;s ReCAPTCHA v3. In all honesty I didn&#39;t realise Cloudflare had a competing product! So, thanks to &lt;a href=&quot;https://bsky.app/profile/twnsnd.com&quot;&gt;Ryan Townsend&lt;/a&gt; and &lt;a href=&quot;https://bsky.app/profile/pawelgrzybek.com&quot;&gt;Paweł Grzybek&lt;/a&gt; for letting me know!&lt;p&gt;I&#39;m not going to be rewriting the whole blog post, but what I will do is share the client-side JavaScript code (&lt;code&gt;turnstile.js&lt;/code&gt;), and the &lt;code&gt;contact.js&lt;/code&gt; file for the API that I am using to integrate with &lt;a href=&quot;https://www.cloudflare.com/en-gb/application-services/products/turnstile/&quot;&gt;Cloudflare Turnstile&lt;/a&gt;. Also, any additional changes I had to make.&lt;p&gt;First the Code:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;dd608f9df6e21ad5e2ec3a37e1e12264&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Lightweight, dependency-free integration for Cloudflare Turnstile.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This script:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - Validates the contact form on the client (for UX only – server still re-validates).&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - Requests an invisible Turnstile token just before submit.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - Injects the token into a hidden field for the server to verify.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// - Tries hard not to block the user if Turnstile is slow to load.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Public Turnstile site key for this widget. This is *not* a secret.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The matching secret key lives on the server and is used during&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// server-side verification in the Cloudflare Pages Function.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SITE_KEY&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0x4AAAAAACpYvR2v9J0i1tr_&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// The contact form we progressively enhance. If it does not exist&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// (for example, on other pages), bail out early.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fs-frm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Hidden token field management&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Turnstile posts its token back to the server in a field that we choose.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The backend expects this exact name when validating the submission.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; tokenInputName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cf-turnstile-response&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; tokenInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;input[name=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;tokenInputName&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;]&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;tokenInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// If the hidden field was not rendered server-side, create it here.&lt;/span&gt;
    tokenInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;input&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tokenInputName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tokenInput&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Accessible error + status UI helpers&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorSummary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-error-summary&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; formStatus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-status&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Show a high-level error message above the form and move focus there so&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// screen-reader users (and keyboard users) are notified.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;showFormError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errorSummary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      errorSummary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errorSummary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errorSummary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Hide and clear the error summary.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearFormError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errorSummary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      errorSummary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errorSummary&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Attach error state to a single field (ARIA attributes + inline message).&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;showFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fieldId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; field &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fieldId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fieldId &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;field&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      field&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-invalid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      field&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-describedby&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fieldId &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errorEl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      errorEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errorEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Clear error state from a single field.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fieldId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; field &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fieldId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorEl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fieldId &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-error&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;field&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      field&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-invalid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      field&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;aria-describedby&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errorEl&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      errorEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errorEl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hidden &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Clear all per-field and summary errors.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clearAllErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;clearFormError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clearFieldError&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Set a small, live-updating status message (polite ARIA live region).&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;formStatus&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      formStatus&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Client-side validation&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// This mirrors the server’s validation rules, but is *only* for UX.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The server still validates everything again.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;clearAllErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; nameField &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; emailField &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; messageField &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;#message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;nameField&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;showFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Please enter your full name.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nameField&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;emailField&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;showFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Please enter your email address.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emailField&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;^[^&#92;s@]+@[^&#92;s@]+&#92;.[^&#92;s@]+$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emailField&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;showFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Please enter a valid email address.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emailField&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;messageField&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;showFieldError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Please enter a message.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;messageField&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; summary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;There is 1 error in the form.&quot;&lt;/span&gt;
          &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;There are &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; errors in the form.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;showFormError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;summary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;focus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Turnstile widget lifecycle&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// `widgetId` is the handle returned by turnstile.render.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// `pendingSubmit` tracks whether we should submit once a token arrives.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// `submitButton` lets us disable / re-enable the call-to-action button.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; widgetId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; pendingSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Called by Turnstile when a token is successfully generated.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onTokenReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// If the user was waiting for the challenge to finish, we can now submit.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pendingSubmit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      pendingSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Called when Turnstile hits an error or we decide the widget is unavailable.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onTokenError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;errorCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    pendingSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Turnstile error:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorCode&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;showFormError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;Verification failed. Please try again. If you use an ad blocker, you may need to allow Cloudflare on this site.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      submitButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      submitButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Send Message&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// If we have a widget instance, reset it so the user can try again.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;widgetId &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; turnstile &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      turnstile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;widgetId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Render an invisible Turnstile widget into the placeholder div.&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The widget itself is not visible – it will run when we call execute().&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;renderTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; container &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;turnstile-container&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Only render once, and only on the contact page where the container exists.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;container &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; widgetId &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; turnstile &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      widgetId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; turnstile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property literal-property&quot;&gt;sitekey&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SITE_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Invisible mode: no explicit checkbox; Cloudflare decides when&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// to prompt for a challenge, if at all.&lt;/span&gt;
        &lt;span class=&quot;token property literal-property&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;invisible&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// We want full control, so we manually call turnstile.execute(widgetId).&lt;/span&gt;
        &lt;span class=&quot;token property literal-property&quot;&gt;execution&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;execute&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property literal-property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; onTokenReceived&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property string-property&quot;&gt;&quot;error-callback&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; onTokenError&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property string-property&quot;&gt;&quot;expired-callback&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// Expired tokens are treated as missing; the server will reject them.&lt;/span&gt;
          tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Wait until the Turnstile script has loaded and the global `turnstile`&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// object is available. We poll for a short period so that a slow network&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// does not permanently block the user.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitForTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; turnstile &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; attempts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; checkInterval &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        attempts&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; turnstile &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checkInterval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attempts &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// Give up after ~10s. At this point we will try to submit&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// without a token and let the server return a clear error.&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;clearInterval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checkInterval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token function&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// As soon as the page is ready and Turnstile is (hopefully) loaded,&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// render the widget so it is ready by the time the user hits submit.&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;waitForTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;renderTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Submit flow&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------------&lt;/span&gt;
  form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;submit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We always take over submission so we can validate + get a token first.&lt;/span&gt;
    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;validateForm&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    submitButton &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;button[type=&quot;submit&quot;]&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;submitButton&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      submitButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;disabled &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      submitButton&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Sending…&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Verifying, please wait.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Make sure the Turnstile library has had a chance to load.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;waitForTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// If we still do not have a widget, try to render one and give the&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// browser a short moment to paint it.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;widgetId &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;renderTurnstile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// If for some reason we already have a valid token (for example,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Turnstile auto-ran earlier), we can just submit the form.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tokenInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;submit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Normal path: ask Turnstile to run in invisible mode. When it finishes,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// it will call onTokenReceived, which will submit the form if&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// `pendingSubmit` is true.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;widgetId &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; turnstile &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;undefined&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      pendingSubmit &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      turnstile&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;widgetId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// We could not talk to Turnstile at all – surface a clear error.&lt;/span&gt;
      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Turnstile widget not available&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;onTokenError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;widget-not-available&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with &lt;a href=&quot;https://gist.github.com/Nooshu/dd608f9df6e21ad5e2ec3a37e1e12264&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fewer comments is available in this gist on GitHub&lt;/a&gt;.&lt;p&gt;Now the updates to the &lt;code&gt;contact.js&lt;/code&gt; file that sits in &lt;code&gt;functions/api/&lt;/code&gt; directory.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;f4655e77defa1cfa875c376e2f85de24&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; escapeHtml &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../../_helpers/escape-html.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Cloudflare Pages Function that powers the contact form.
 *
 * High‑level flow (perfect for a blog diagram):
 *
 * 1. Accept a classic `&amp;lt;form&gt;` POST (urlencoded or multipart).
 * 2. Validate the payload on the server (required fields + length limits).
 * 3. Verify a Cloudflare Turnstile token server‑side.
 * 4. Send a notification email via Resend (HTTPS API call).
 * 5. Redirect the user to a static “thanks” page.
 *
 * The matching HTML form does *not* need JavaScript – the worker stands on its
 * own. Client‑side JS (for validation + Turnstile widget) is purely a UX bonus.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function function-variable&quot;&gt;onRequestPost&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 1. Only accept traditional browser form submissions&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The contact page posts using `application/x-www-form-urlencoded`, but&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// we also support `multipart/form-data` so the handler works with&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// progressive enhancement and file inputs if they ever appear.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; contentType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;contentType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;application/x-www-form-urlencoded&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;contentType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;multipart/form-data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unsupported content type&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;415&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// This gives us a `FormData` instance regardless of which of the two&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// encodings the browser chose.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; form &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 2. Honeypot – cheap, early bot filter&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The form includes a visually hidden checkbox with this wonderfully&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ugly name. Real users never see or tick it, but naive bots often&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// will. If it has a value, we quietly redirect back to the homepage.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_its-a-trap!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hpUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hpUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;303&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;name&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;email&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;message&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; redirectUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_redirect&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/contact/thanks/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 3. Field‑level validation (server‑side, regardless of JS on client)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;email &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Missing required fields&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Upper bounds on field length keep logs and emails sane and protect&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// downstream services from unexpectedly huge payloads.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;320&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Invalid field lengths&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 4. Verify Cloudflare Turnstile token (server‑side)&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// The front‑end Turnstile widget places its token into&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// `cf-turnstile-response`. Without a token, we *always* reject the&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// submission – even if the rest of the payload looks valid.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; turnstileToken &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;cf-turnstile-response&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;turnstileToken&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Turnstile token is required&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// The Turnstile secret is never exposed to the client – it lives in&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Cloudflare Pages environment variables.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; secret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;TURNSTILE_SECRET_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Server not configured for CAPTCHA (missing TURNSTILE_SECRET_KEY)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Including the remote IP is optional but recommended; it gives&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Turnstile a little more context when scoring the request.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; remoteIp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CF-Connecting-IP&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cf&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;clientAddress &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyBody &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			secret&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; turnstileToken&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;remoteIp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			verifyBody&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;remoteip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; remoteIp&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyResp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://challenges.cloudflare.com/turnstile/v0/siteverify&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;verifyBody&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;verifyResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Failed to verify Turnstile&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;502&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; verifyJson &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; verifyResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// If Turnstile says “no”, do not send an email – fail fast here.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;success&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorCodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; verifyJson&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;error-codes&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;CAPTCHA verification failed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; errorCodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; errorCodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;, &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Unknown error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;400&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Optional defence‑in‑depth check: ensure the token we received was&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// minted for this hostname (or one of its subdomains).&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; requestHost &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt;
			verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; requestHost &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt;
			&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;requestHost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Turnstile hostname mismatch: expected &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;requestHost&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;, got &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;verifyJson&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;hostname&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 5. Build notification email payload for Resend&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// `fromEmail` must be a sender you control and have verified with Resend.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fromEmail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;email@example.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; subject &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;_email.subject&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;New Message from Nooshu.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; textBody &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;New contact form submission on nooshu.com&#92;n&#92;nName: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;nEmail: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;email&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#92;n&#92;nMessage:&#92;n&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;message&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlBody &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;h2&gt;New contact form submission on nooshu.com&amp;lt;/h2&gt;&amp;lt;p&gt;&amp;lt;strong&gt;Name:&amp;lt;/strong&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;br/&gt;&amp;lt;strong&gt;Email:&amp;lt;/strong&gt; &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;email&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/p&gt;&amp;lt;p&gt;&amp;lt;strong&gt;Message:&amp;lt;/strong&gt;&amp;lt;/p&gt;&amp;lt;pre style=&quot;white-space:pre-wrap&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;escapeHtml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/pre&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Resend API key comes from Cloudflare Pages environment variables,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// never from the client or build‑time `.env`.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; apiKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;RESEND_API_KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;apiKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
				&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Server not configured for email (missing RESEND_API_KEY)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Call the Resend REST API. In this project we make a single,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// straightforward `emails` call – no templates, CC/BCC, or attachments.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; resendResp &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://api.resend.com/emails&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;POST&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;Authorization&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Bearer &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;apiKey&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Nooshu Contact &amp;lt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;fromEmail&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;email@example.com&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;reply_to&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; email&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				subject&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; textBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; htmlBody&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;resendResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; resendResp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Email send failed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; text &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;502&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
				&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// 6. Redirect to “thanks” page&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// ---------------------------------------------------------------------&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Using a 303 ensures the follow‑up request is a GET, which means&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// browser refreshes do not re‑POST the form.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalRedirect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;redirectUrl&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalRedirect&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;303&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Server error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property literal-property&quot;&gt;details&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token property literal-property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property string-property&quot;&gt;&#39;content-type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with &lt;a href=&quot;https://gist.github.com/Nooshu/f4655e77defa1cfa875c376e2f85de24&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;fewer comments is available in this gist on GitHub&lt;/a&gt;.&lt;p&gt;Amazingly, that&#39;s really the only two major changes!&lt;p&gt;The minor changes were:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Footer template&lt;/strong&gt;: Update my footer with a link to the JavaScript above and the Turnstile JavaScript, e.g. &lt;code&gt;https://challenges.cloudflare.com/turnstile/v0/api.js&lt;/code&gt;.&lt;li&gt;&lt;strong&gt;CSP&lt;/strong&gt;: Modified my &lt;code&gt;_headers&lt;/code&gt; file to remove Google from my CSP and add in the challenges URL (https://challenges.cloudflare.com/) to the &lt;code&gt;connect-src&lt;/code&gt;, &lt;code&gt;script-src-elem&lt;/code&gt;, and &lt;code&gt;frame-src&lt;/code&gt;.&lt;li&gt;&lt;strong&gt;CSS&lt;/strong&gt;: Slightly modify the CSS to remove Google ReCAPTCHA v3 specific styling (&lt;code&gt;.grecaptcha-badge&lt;/code&gt;) and add Turnstile CSS (&lt;code&gt;.turnstile-widget&lt;/code&gt;).&lt;li&gt;&lt;strong&gt;Secret Keys&lt;/strong&gt;: RECAPTCHA_SECRET_KEY swapped out for the TURNSTILE_SECRET_KEY in the Cloudflare Pages environment variables settings page.&lt;li&gt;&lt;strong&gt;Contact Form HTML&lt;/strong&gt;: HTML changes to the contact form added cf-turnstile-response hidden &lt;code&gt;&amp;lt;input&gt;&lt;/code&gt; and #turnstile-container &lt;code&gt;&amp;lt;div&gt;&lt;/code&gt;.&lt;/ol&gt;&lt;h3 id=&quot;why-change&quot;&gt;Why change?&lt;/h3&gt;&lt;p&gt;So I&#39;ve literally done a 180 from Google ReCAPTCHA v3 to Cloudflare Turnstile in less than a week. So why change and what are the differences? The top of my priority list that I admittedly should have considered when integrating Google ReCAPTCHA v3 is privacy! I would rather not subject any readers of the blog to any additional tracking from Google! But there are other differences too:&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Area&lt;th&gt;Cloudflare Turnstile advantage over reCAPTCHA v3&lt;th&gt;Why it matters&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Privacy&lt;td&gt;Turnstile is positioned as a privacy preserving alternative, and it does not rely on tracking user data across sites for ad retargeting. It also offers Ephemeral IDs, which are short-lived device identifiers that work without cookies or client-side storage.&lt;td&gt;Better fit for teams with strong privacy, GDPR, or public sector concerns.&lt;tr&gt;&lt;td&gt;Less operational tuning&lt;td&gt;Turnstile’s server validation is a straightforward success or failure check. By contrast, reCAPTCHA v3 returns a score and Google recommends that you review scores in the admin console and tune thresholds based on your own traffic, starting from 0.5.&lt;td&gt;Usually simpler to ship and maintain, especially for smaller teams that do not want to spend time calibrating fraud thresholds.&lt;tr&gt;&lt;td&gt;No CAPTCHA experience by default&lt;td&gt;Turnstile works without showing visitors a CAPTCHA and supports invisible, non-interactive, and managed modes. reCAPTCHA v3 is also frictionless, but Turnstile is explicitly built as a CAPTCHA replacement with more presentation modes for the widget itself.&lt;td&gt;More control over UX and easier to keep forms feeling clean while still validating traffic.&lt;tr&gt;&lt;td&gt;Can be used on any site without Cloudflare CDN&lt;td&gt;Turnstile can be embedded into any website, it does not require your traffic to go through Cloudflare or use Cloudflare’s CDN.&lt;td&gt;Easier adoption if you want the bot check without changing your hosting or edge setup.&lt;tr&gt;&lt;td&gt;Developer-friendly testing&lt;td&gt;Turnstile provides dummy site keys and secret keys for testing, and Cloudflare documents that these work on localhost and any development domain. Google’s reCAPTCHA FAQ says for v3 you should create a separate key for testing environments, and scores may not be accurate because v3 relies on real traffic.&lt;td&gt;Usually a smoother local dev and automated test setup.&lt;tr&gt;&lt;td&gt;Free plan positioning&lt;td&gt;Cloudflare documents Turnstile has a Free plan intended for personal sites, SMBs, dev and test, and most production applications. Google’s FAQ notes quota limits for non-Enterprise reCAPTCHA, and if a v3 key exceeds a monthly quota it may fail open with a static score of 0.9 for the remainder of the month.&lt;td&gt;Attractive when cost certainty matters or when you want fewer surprises as usage grows.&lt;tr&gt;&lt;td&gt;Extra integration option with Cloudflare security stack&lt;td&gt;Turnstile supports pre-clearance, letting it issue clearance cookies that can be used across Cloudflare protected domains.&lt;td&gt;Helpful if you already use Cloudflare WAF or bot controls and want tighter integration.&lt;/table&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;Well, that brings us to the end of another blog post, I only went off on a tangent a couple of times! So well done for sticking with my ramblings!&lt;p&gt;In the end, the goal was to keep the architecture simple while still covering the essentials. The site itself remains fully static, with a lightweight Cloudflare Worker handling form submissions. Spam protection is provided through &lt;del&gt;Google reCAPTCHA v3&lt;/del&gt;, Cloudflare Turnstile, while validation and accessibility patterns ensure the form behaves reliably for real users. It is a small piece of functionality, but implemented carefully it can be both robust and easy to maintain.&lt;p&gt;As always, thanks for reading, and I’d love to hear your feedback. You are welcome to contact me either via the &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;contact form&lt;/a&gt; (again, very meta! 😏), or via any of the various social channels listed on the site.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;09/03/26: Initial post published.&lt;li&gt;13/03/26: Updates to give instructions on how to use Cloudflare Turnstile as an alternative to Google reCAPTCHA v3. Thanks again to &lt;a href=&quot;https://bsky.app/profile/twnsnd.com&quot;&gt;Ryan Townsend&lt;/a&gt; and &lt;a href=&quot;https://bsky.app/profile/pawelgrzybek.com&quot;&gt;Paweł Grzybek&lt;/a&gt;.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Precompressed HTML at the Edge: Eleventy Meets Cloudflare Workers</title>
    <link href="https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/" />
    <updated>2026-02-21T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/</id>
    <content type="html">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;In 2025, I wrote a series of web performance optimisation blog posts focussing on some of the key fundamental&#39;s of Frontend Web Performance:&lt;h3 id=&quot;caching&quot;&gt;Caching&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/09/02/asset-fingerprinting-and-the-preload-response-header-in-11ty/&quot;&gt;Asset fingerprinting and the preload response header in 11ty&lt;/a&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;p&gt;The blog post describes how the I enhanced web performance on my 11ty-built site by combining asset fingerprinting with the HTTP preload hint.&lt;p&gt;It explains that preload tells the browser to fetch critical resources earlier, but hashed filenames make this difficult to manage manually.&lt;p&gt;The solution was to generate preload Link headers automatically during the 11ty build. A custom script locates the fingerprinted CSS file and injects the correct preload header into the Cloudflare Pages &lt;code&gt;_headers&lt;/code&gt; file.&lt;p&gt;This speeds up CSS delivery, removes the need for manual updates, and allows the use of long-lived &lt;code&gt;Cache-Control&lt;/code&gt; header values such as &lt;code&gt;max-age=31536000&lt;/code&gt; and &lt;code&gt;immutable&lt;/code&gt;.&lt;h3 id=&quot;compression&quot;&gt;Compression&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/05/cranking-brotli-up-to-11-with-cloudflare-pro-and-11ty/&quot;&gt;Cranking Brotli up to 11 with Cloudflare Pro and 11ty&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;p&gt;The blog post explains how I improved the performance of my 11ty-powered blog after migrating to Cloudflare Pages by using Brotli compression at the highest level (11) for static assets. I also describes the difference between Brotli and gzip, outline how Cloudflare’s Pro plan typically applies a moderate Brotli level (4), and then show how to pre-compress JavaScript files to Brotli level 11. Lastly, serve them correctly both locally and via Cloudflare, and configure Cloudflare’s compression rules so that all assets benefit from the stronger compression to reduce file sizes and improve load performance.&lt;h3 id=&quot;concatenation&quot;&gt;Concatenation&lt;/h3&gt;&lt;p&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/&quot;&gt;Using an 11ty Shortcode to craft a custom CSS pipeline&lt;/a&gt;&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;p&gt;While not strictly about concatenation, it covers related ideas. The post explains how I built a custom CSS pipeline for my 11ty site using a bespoke short code rather than the default bundle plugin.&lt;p&gt;It details how I preserved local live reload, added content-based fingerprinting for long-term caching, minified CSS with &lt;a href=&quot;https://www.npmjs.com/package/clean-css&quot;&gt;clean-css package&lt;/a&gt;, enabled Brotli compression, and used disk and memory caching to prevent unnecessary work.&lt;p&gt;The build also generates hashed filenames and matching HTML link tags, ensuring production serves fully optimised, cache friendly CSS automatically.&lt;h2 id=&quot;more-compression&quot;&gt;More compression&lt;/h2&gt;&lt;p&gt;In this blog post, I’m going to take the compression a little further. I already have CSS and JavaScript Brotli compressed to the highest level (11) and served from Cloudflare Pages. But what about the third and final core technology of the web? Arguably the most important too… The HTML. In this post, I will look at how to compress your HTML to 11 during the 11ty build phase, and what modern technologies you need in order to make this work (it’s not as straightforward as I thought!)&lt;h2 id=&quot;why-html-brotli-matters&quot;&gt;Why HTML Brotli matters?&lt;/h2&gt;&lt;p&gt;Before we dive into the details, let’s discuss why Brotli compression is important for HTML. Well, to summarise Brotli compression in one sentence:&lt;blockquote&gt;&lt;p&gt;Brotli compression reduces file size by intelligently identifying repeated patterns in data and encoding them more efficiently using a combination of modern compression techniques.&lt;/blockquote&gt;&lt;p&gt;This is fantastic for anything with repeating patterns as the bytes saved over the network can be huge! The great thing about HTML is that it has a tonne of repeating patterns (e.g. Markup)! This reduction in file size &lt;strong&gt;could&lt;/strong&gt; potentially equate to:&lt;h3 id=&quot;improved-web-performance&quot;&gt;Improved Web Performance&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;Lower Time To First Byte impact on slow networks&lt;li&gt;Improved &lt;a href=&quot;https://web.dev/articles/fcp&quot;&gt;First Contentful Paint&lt;/a&gt;&lt;li&gt;Improved &lt;a href=&quot;https://web.dev/articles/lcp&quot;&gt;Largest Contentful Paint&lt;/a&gt;&lt;li&gt;Reduced &lt;a href=&quot;https://web.dev/articles/tbt&quot;&gt;Total Blocking Time&lt;/a&gt; (indirectly)&lt;/ul&gt;&lt;p&gt;Every kilobyte saved here multiplies across your traffic volume.&lt;h3 id=&quot;at-scale-cost-savings-add-up&quot;&gt;At scale, cost savings add up&lt;/h3&gt;&lt;p&gt;Let’s assume you are serving a high traffic website, say the &lt;a href=&quot;https://www.bbc.co.uk&quot;&gt;BBC&lt;/a&gt;, &lt;a href=&quot;https://www.google.com&quot;&gt;Google&lt;/a&gt;, or &lt;a href=&quot;https://www.gov.uk&quot;&gt;GOV.UK&lt;/a&gt;. Imagine how much bandwidth could be saved by simply compressing your HTML. Any percentage saving on millions of requests per day is going to mean:&lt;ul&gt;&lt;li&gt;Less bandwidth&lt;li&gt;Lower &lt;a href=&quot;https://www.cloudflare.com/en-gb/learning/cloud/what-are-data-egress-fees/&quot;&gt;CDN egress&lt;/a&gt;&lt;li&gt;Lower cloud costs&lt;li&gt;Lower carbon footprint&lt;/ul&gt;&lt;p&gt;This is a win both commercially and environmentally!&lt;h3 id=&quot;improved-performance-on-slow-and-unstable-mobile-networks&quot;&gt;Improved performance on slow and unstable mobile networks&lt;/h3&gt;&lt;p&gt;Brotli 11 improves the web for users where they need it the most:&lt;ul&gt;&lt;li&gt;3G connectivity&lt;li&gt;High latency rural connections&lt;li&gt;Congested public networks&lt;li&gt;International access&lt;/ul&gt;&lt;p&gt;HTML blocks everything else. If you shrink it aggressively, you unblock the page faster, meaning users get a better experience.&lt;p&gt;This is a real-world performance gain.&lt;h3 id=&quot;it-indirectly-improves-core-web-vitals&quot;&gt;It indirectly improves Core Web Vitals&lt;/h3&gt;&lt;p&gt;Smaller HTML means:&lt;ul&gt;&lt;li&gt;Faster DOM construction&lt;li&gt;Earlier CSS discovery&lt;li&gt;Earlier JS discovery&lt;li&gt;Reduced main thread idle gaps&lt;/ul&gt;&lt;p&gt;This is especially important for server rendered pages or hybrid Server-side Rendered apps.&lt;h3 id=&quot;compression-cost-is-paid-once-for-static-assets&quot;&gt;Compression cost is paid once for static assets&lt;/h3&gt;&lt;p&gt;Yes, level 11 compression is expensive to compute. But this cost is paid back over time. You pay for the CPU time once. Users benefit forever assuming:&lt;ul&gt;&lt;li&gt;the HTML is static and cacheable at the Content Delivery Network (CDN) using long-life caching headers&lt;li&gt;you automate and pre-compress the HTML at build time&lt;/ul&gt;&lt;h3 id=&quot;compression-size-examples-v1&quot;&gt;Compression Size Examples v1&lt;/h3&gt;&lt;p&gt;Let’s have a look at a huge HTML page on the web. My go-to for this is either the &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-20110405/Overview.html&quot;&gt;W3C HTML5 Specification page&lt;/a&gt; OR &lt;a href=&quot;https://apod.nasa.gov/apod/archivepix.html&quot;&gt;NASA’s Astronomy Picture of the Day Archive&lt;/a&gt;.&lt;p&gt;Rather than choose, let’s just compress both!&lt;h4 id=&quot;w3c-html5-specification-single-page&quot;&gt;W3C HTML5 Specification (Single Page)&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-20110405/Overview.html&quot;&gt;https://www.w3.org/TR/2011/WD-html5-20110405/Overview.html&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Uncompressed size&lt;/strong&gt;: 4.7 MB&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 590 KB (~88% saving)&lt;/ul&gt;&lt;p&gt;That’s an &lt;strong&gt;88 percent reduction&lt;/strong&gt; in bytes over the network when compressing the HTML with Brotli 11. Not bad for something that takes just over 10-seconds to run.&lt;p&gt;And yes, that’s 4.7 MB of HTML alone. It’s an absolute beast of a page.&lt;p&gt;For perspective, that would take around 12 to 15 minutes to download on a 56k modem in the late 90s. Just for the HTML. I might be showing my age here 😭&lt;h4 id=&quot;nasa-s-astronomy-picture-of-the-day-archive&quot;&gt;Nasa’s Astronomy Picture of the Day Archive&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://apod.nasa.gov/apod/archivepix.html&quot;&gt;https://apod.nasa.gov/apod/archivepix.html&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Uncompressed size&lt;/strong&gt;: 314 KB&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 53 KB (83% saving)&lt;/ul&gt;&lt;p&gt;We’ve taken it from roughly a third of a megabyte down to just &lt;strong&gt;53 KB&lt;/strong&gt;. That is a pretty substantial reduction!&lt;p&gt;This means faster page loads, lower data usage, and a much smoother experience for anyone on a limited data plan or dealing with patchy connections. A very positive impact, right where it matters most for users.&lt;h2 id=&quot;how-is-this-different-from-my-other-compression-posts&quot;&gt;How is this different from my other compression posts?&lt;/h2&gt;&lt;p&gt;So how is this different from the Brotli compression I have mentioned in the previous blog posts?&lt;p&gt;In &lt;a href=&quot;https://nooshu.com/blog/2025/01/05/cranking-brotli-up-to-11-with-cloudflare-pro-and-11ty/&quot;&gt;Cranking Brotli up to 11 with Cloudflare Pro and 11ty&lt;/a&gt; I used a combination of:&lt;ul&gt;&lt;li&gt;Brotli CLI (e.g.&lt;code&gt;brew install brotli&lt;/code&gt;)&lt;li&gt;bash scripts (&lt;code&gt;compress.sh&lt;/code&gt;, &lt;code&gt;compress-directory.sh&lt;/code&gt;)&lt;/ul&gt;&lt;p&gt;In this post, I override the Cloudflare Dashboard&#39;s &quot;Compression Rules” (which dynamically compresses HTML at Brotli level 4). The CDN is compressing the HTML on-the-fly when the user’s browser requests it.&lt;p&gt;What this blog post describes is pre-compression to Brotli 11 at 11ty build time. To do this the workflow is:&lt;ul&gt;&lt;li&gt;Apply Brotli level 11 pre-compression to HTML during the 11ty build on Cloudflare Pages.&lt;li&gt;&lt;code&gt;_helpers/html-compression.js&lt;/code&gt; runs after the site is written to &lt;code&gt;_site&lt;/code&gt;.&lt;li&gt;Each HTML file gets a matching &lt;code&gt;.br&lt;/code&gt; file&lt;li&gt;Use the built-in Node &lt;code&gt;zlib&lt;/code&gt; module, so no manual setup, CLI tools, scripts, Cloudflare configuration, or extra dependencies are required.&lt;li&gt;Take advantage of &lt;a href=&quot;https://developers.cloudflare.com/pages/functions/&quot;&gt;Cloudflare Pages Functions&lt;/a&gt;, which run on &lt;a href=&quot;https://workers.cloudflare.com/&quot;&gt;Cloudflare Workers&lt;/a&gt;. This is a great opportunity to use a modern, fast evolving platform that opens the door to powerful edge capabilities.&lt;/ul&gt;&lt;h2 id=&quot;what-s-the-point&quot;&gt;What’s the point?&lt;/h2&gt;&lt;p&gt;Well, that’s a great question. As some readers may know by default Cloudflare compresses HTML at compression level 4. Now, if we compare this level to the compression level 11 above, let’s have a look at the difference:&lt;h3 id=&quot;compression-size-examples-v2&quot;&gt;Compression Size Examples v2&lt;/h3&gt;&lt;h4 id=&quot;w3c-html5-specification-single-page-2&quot;&gt;W3C HTML5 Specification (Single Page)&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://www.w3.org/TR/2011/WD-html5-20110405/Overview.html&quot;&gt;https://www.w3.org/TR/2011/WD-html5-20110405/Overview.html&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Uncompressed size&lt;/strong&gt;: 4.7 MB&lt;li&gt;&lt;strong&gt;Brotli (Level 4)&lt;/strong&gt;: 729 KB (83% saving)&lt;li&gt;&lt;strong&gt;Compression Time (Level 4)&lt;/strong&gt;: 0.149 seconds&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 590 KB (87% saving)&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 11.717 seconds&lt;/ul&gt;&lt;h4 id=&quot;nasa-s-astronomy-picture-of-the-day-archive-2&quot;&gt;Nasa’s Astronomy Picture of the Day Archive&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;URL&lt;/strong&gt;: &lt;a href=&quot;https://apod.nasa.gov/apod/archivepix.html&quot;&gt;https://apod.nasa.gov/apod/archivepix.html&lt;/a&gt;&lt;li&gt;&lt;strong&gt;Uncompressed size&lt;/strong&gt;: 314 KB&lt;li&gt;&lt;strong&gt;Brotli (Level 4)&lt;/strong&gt;: 66 KB (79%)&lt;li&gt;&lt;strong&gt;Compression Time (Level 4)&lt;/strong&gt;: 0.011 seconds&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 53 KB (83% saving)&lt;li&gt;&lt;strong&gt;Brotli (Level 11)&lt;/strong&gt;: 0.743 seconds&lt;/ul&gt;&lt;p&gt;It’s worth noting that I used &lt;a href=&quot;https://paulcalvano.com/&quot;&gt;Paul Calvano&#39;s&lt;/a&gt; fantastic &lt;a href=&quot;https://tools.paulcalvano.com/compression-tester/&quot;&gt;Compression Tester Tool&lt;/a&gt;, to help with this basic analysis.&lt;p&gt;What you will likely notice is that even for large HTML files the size difference between compression level 4 and compression level 11 isn’t huge, only 12 KB in the W3C HTML5 Specification example. The real difference comes in computation time. Level 4 0.149 seconds verses 11.717 seconds! That’s a &lt;strong&gt;7764% increase&lt;/strong&gt; in time between Level 4 and Level 11! Although this is an extreme example, you can probably see why Level 11 isn’t used by Cloudflare for on-the-fly compression of HTML assets. Level 4 gives a good balence between file compression versus compression speed. I’m betting countless smart people were involved in the analysis of using Level 4 by default! When you are a company that is literally serving billions of requests per second, this decision really makes a difference in terms of processing power infrastructure and power usage!&lt;p&gt;Thankfully, the way that I have implemented level 11 compression on my blog, processing time doesn’t really matter. All the HTML is being compressed at 11ty build time. As I said above, the cost of this additional CPU time is paid back over time by users getting a better experience (even if only slightly). Furthermore, remember there’s a slight reduction in storage required on the CDN. From my perspective, if it is low effort after setup, it feels like the right move to implement it.&lt;h2 id=&quot;problems&quot;&gt;Problems&lt;/h2&gt;&lt;p&gt;As I found out during implementation it isn’t just as simple as compressing the HTML to 11 and setting a static &lt;code&gt;Content-Encoding&lt;/code&gt; header for HTML in the &lt;code&gt;_headers&lt;/code&gt; file (trust me, I tried it!)&lt;h3 id=&quot;problem-1-url-path-vs-actual-file-path-mismatch&quot;&gt;Problem 1: URL Path vs Actual File Path Mismatch&lt;/h3&gt;&lt;p&gt;When serving static assets like CSS, JS, or images, there is usually a simple one-to-one relationship between the URL and the file on disk. A request to &lt;code&gt;/css/site.css&lt;/code&gt; maps directly to &lt;code&gt;_site/css/site.css&lt;/code&gt;. No extra logic is required because the URL path matches the file path exactly. I had no idea, but I soon found out that HTML pages behave differently. A request to &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;/blog/post/&lt;/code&gt; does not correspond to a literal file at that path. Instead, the server applies a convention and serves &lt;code&gt;index.html&lt;/code&gt; inside that directory. So &lt;code&gt;/blog/post/&lt;/code&gt; actually maps to &lt;code&gt;blog/post/index.html&lt;/code&gt; on disk. This mapping happens automatically when Cloudflare serves uncompressed HTML through its very efficient static asset layer.&lt;p&gt;The problem appears when serving pre-compressed Brotli files. You cannot simply request the same URL and expect the .br file to resolve. Instead, the Cloudflare Function must manually translate the directory style URL into the real file path before appending &lt;code&gt;.br&lt;/code&gt;.&lt;p&gt;For example, &lt;code&gt;/&lt;/code&gt; becomes &lt;code&gt;/index.html.br&lt;/code&gt;, &lt;code&gt;/blog/post/&lt;/code&gt; becomes &lt;code&gt;/blog/post/index.html.br&lt;/code&gt;, and &lt;code&gt;/404.html&lt;/code&gt; becomes &lt;code&gt;/404.html.br&lt;/code&gt;.&lt;p&gt;In short, HTML requires explicit path translation because the browser sees a directory style URL while the actual file stored on disk is index.html. The Cloudflare Function must bridge that gap to correctly serve the Brotli 11 compressed version, rather than the uncompressed HTML version.&lt;p&gt;It actually makes sense now that I think about it, I’ve always just taken the automatic appending of &lt;code&gt;index.html&lt;/code&gt; to a URL Path for granted! The fact that as a user on the web doesn’t even have to think about that small detail, shows how well it works! As &lt;a href=&quot;https://en.wikipedia.org/wiki/Dieter_Rams&quot;&gt;Dieter Rams&lt;/a&gt; once said:&lt;blockquote&gt;&lt;p&gt;Good design is as little design as possible.&lt;/blockquote&gt;&lt;h3 id=&quot;problem-2-a-page-full-of-wingdings&quot;&gt;Problem 2: A page full of Wingdings&lt;/h3&gt;&lt;p&gt;I’m showing my age again, but for readers who don’t remember early versions of Windows (e.g. &lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_3.1&quot;&gt;3.1&lt;/a&gt;), it came bundled with a font called &lt;a href=&quot;https://en.wikipedia.org/wiki/Wingdings&quot;&gt;Wingdings&lt;/a&gt;. This True Type font contains many largely recognised shapes and gestures as well as some recognised world symbols.&lt;p&gt;Wingdings were an early symbol font that experimented with pictographic digital symbols, that would later lead on to ASCII emoticons like &lt;code&gt;:-)&lt;/code&gt; &amp; &lt;code&gt;¯&#92;_(ツ)_/¯&lt;/code&gt;, which in turn would progress to the modern world of &lt;a href=&quot;https://emojipedia.org/&quot;&gt;Emoji’s&lt;/a&gt;!&lt;p&gt;Essentially, what was happening the Cloudflare server was serving raw Brotli compressed HTML files to the browser, expecting it to understand what these (now binary, not text) files were, and how to read and understand them. I was essentially serving the HTML without the following headers:&lt;pre&gt;&lt;code&gt;Content-Type: text/html; charset=UTF-8
Content-Encoding: br
Vary: Accept-Encoding
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here’s a brief explanation of these headers:&lt;ol&gt;&lt;li&gt;&lt;code&gt;Content-Encoding: br&lt;/code&gt;: This is telling the browser “What I’m sending you is a Brotli compressed file, you are going to need to decode it before you understand it”.&lt;li&gt;&lt;code&gt;Content-Type: text/html; charset=UTF-8&lt;/code&gt;: This tells the browser what character set (&#39;charset&#39;) it should use after decompression, this is essential as this is key to the browser parsing the HTML correctly.&lt;li&gt;&lt;code&gt;Vary: Accept-Encoding&lt;/code&gt; only affects caching (e.g. Content Delivery Networks). It’s basically saying to the cache to store separate versions of this file depending on the Accept-Encoding header.&lt;/ol&gt;&lt;p&gt;The result of the above gave me a homepage that looked like the image below (interesting but not exactly readable!):&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-300.webp 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-600.webp 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-814.webp 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-1200.webp 1200w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The garbage output of a raw Brotli encoded HTML file in the browser looks like something from the Wingdings font from the early 90’s&quot; decoding=&quot;async&quot; height=&quot;670&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-300.jpeg&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-300.jpeg 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-600.jpeg 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-814.jpeg 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/so0HRc7rVz-1200.jpeg 1200w&quot; width=&quot;1200&quot;&gt;&lt;/picture&gt;&lt;h2 id=&quot;my-approach&quot;&gt;My Approach&lt;/h2&gt;&lt;h3 id=&quot;step-1-build-time-compression&quot;&gt;Step 1: Build-time compression&lt;/h3&gt;&lt;p&gt;After the site is generated, it moves into a final preparation stage before going live.&lt;p&gt;During this stage, the system goes through all the finished HTML pages and creates highly compressed versions of them. These compressed files sit alongside the originals and are ready to be served immediately.&lt;p&gt;Because this happens as part of the 11ty build process, every release automatically includes freshly optimised files. This means the live site can deliver pages faster, with smaller file sizes and no need to compress anything on the fly (e.g. what Cloudflare does with its HTML compression to Brotli level 4).&lt;p&gt;The result is better performance for users, with no extra overhead once the site is hosted and running on Cloudflare pages.&lt;h3 id=&quot;step-2-cloudflare-pages-function-for-content-negotiation&quot;&gt;Step 2: Cloudflare Pages Function for content negotiation&lt;/h3&gt;&lt;p&gt;When someone visits a page on the site, a lightweight Cloudflare Function (via a Cloudflare Worker) checks whether a users browser supports modern compression.&lt;p&gt;If it does, the system serves the Brotli 11 pre compressed version of the page. This keeps file sizes small and pages loading quickly.&lt;p&gt;If the browser does not support this compression, or if a compressed version is not available, the system simply serves the standard uncompressed version of the HTML instead.&lt;p&gt;No extra processing happens at this stage. The edge layer (Cloudflare Function + Worker) is only deciding which version of the already prepared files to send. All optimisation work has already been done earlier in the 11ty build process.&lt;p&gt;The final result is fast delivery, efficient bandwidth use, and a simple, reliable build setup. Everyone wins!&lt;h3 id=&quot;step-3-the-eleventy-build-uses-node-js-zlib-for-seamless-integration&quot;&gt;Step 3: The Eleventy build uses Node.js zlib, for seamless integration&lt;/h3&gt;&lt;p&gt;As mentioned earlier, this Brotli implementation differs from others I have used. Instead of relying on bash scripts such as &lt;code&gt;compress.sh&lt;/code&gt; or &lt;code&gt;compress-directory.sh&lt;/code&gt;, it uses Node.js’s built in &lt;code&gt;zlib&lt;/code&gt; module. Because &lt;a href=&quot;https://nodejs.org/dist/latest/docs/api/zlib.html#zlib&quot;&gt;zlib is part of Node.js core&lt;/a&gt;, it is stable, well maintained, and requires no external dependencies. It has been available since the earliest Node.js releases, so it is a sensible default choice. I may even revisit the other build process in future and consider replacing the remaining bash scripts with a fully Node.js based approach.&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;&lt;p&gt;Next, let’s stop the waffling and get onto the actual implementation, I assume that’s what readers are here for after all!&lt;h3 id=&quot;core-compression-utility&quot;&gt;Core compression utility&lt;/h3&gt;&lt;p&gt;Here is the main compression file that is used to compress the HTML to Brotli 11:&lt;pre class=&quot;language-js&quot; data-gist=&quot;0dd55a4ba67da0a6d0053dc0e2884ba8&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * Core compression utility: Brotli compression for build-time pre-compression.
 *
 * This module is the single source of truth for Brotli in the project. It is used by:
 * - html-compression.js  — compresses all HTML in _site to .br (level 11)
 * - css-manipulation.js  — compresses processed CSS to .br when writing to _site
 * - js-compression.js    — compresses minified JS in _site/js to .br
 *
 * All compression happens during the Eleventy production build (postbuild phase).
 * Pre-compressed .br files are then served via content negotiation in functions/[[path]].js
 * when the client sends Accept-Encoding: br, avoiding any runtime or CDN dynamic compression.
 */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; brotliCompressSync &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;zlib&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Default Brotli compression level.
 *
 * Brotli levels range from 0 to 11:
 * - 0–3: Fast, lower ratio (typical for dynamic/on-the-fly compression; e.g. CDNs often use 4).
 * - 11:  Maximum ratio, slowest; ideal for static assets compressed once at build time.
 *
 * We use 11 because compression runs only during the build, so CPU cost is paid once per
 * deploy. The resulting .br files are then served as-is with no re-compression at the edge.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BROTLI_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Compress input data with Brotli.
 *
 * Uses Node&#39;s synchronous zlib API so callers can use the function in a simple, blocking
 * way during the build (no need to await). Input is normalized to a Buffer so that
 * strings (e.g. file contents read as UTF-8) and TypedArrays are supported.
 *
 * @param {Buffer | Uint8Array | string} input - Data to compress. If a string, it is
 *   encoded as UTF-8. Buffers and Uint8Array are used as-is (after copying to a Buffer
 *   when necessary via Buffer.from).
 * @param {number} [level=BROTLI_LEVEL] - Compression level 0–11. Defaults to 11 for
 *   maximum ratio. Can be overridden (e.g. via BROTLI_COMPRESSION_LEVEL in css-manipulation).
 * @returns {Buffer} - Compressed data as a Buffer. Callers typically write this to a
 *   file with a .br extension (e.g. index.html.br).
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;brotliCompress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; level &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BROTLI_LEVEL&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isBuffer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; input &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;brotliCompressSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; level &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I appreciate that not everyone prefers heavily commented code, so there is also a version in &lt;a href=&quot;https://gist.github.com/Nooshu/0dd55a4ba67da0a6d0053dc0e2884ba8&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this Gist&lt;/a&gt; with minimal comments and improved readability.&lt;h3 id=&quot;html-compression-post-build&quot;&gt;HTML compression post-build&lt;/h3&gt;&lt;p&gt;The post-build HTML compression file:&lt;pre class=&quot;language-js&quot; data-gist=&quot;568f44fe571d11660bc3e4e281f6329b&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * HTML Brotli compression (postbuild).
 *
 * This module compresses every HTML file in the Eleventy output directory (_site) to
 * Brotli level 11 and writes a corresponding .br file alongside each .html file
 * (e.g. index.html → index.html.br, blog/post/index.html → blog/post/index.html.br).
 *
 * When it runs:
 *   Only during the production postbuild phase, after Eleventy has finished writing
 *   _site. It is invoked from _config/build-events.js in the eleventy.after handler
 *   (alongside JS minification, JS Brotli, and preload header generation).
 *
 * How .br files are served:
 *   The Cloudflare Pages Function functions/[[path]].js performs content negotiation
 *   for HTML document requests. When the client sends Accept-Encoding: br, the Function
 *   fetches the pre-built .br asset (e.g. / → index.html.br) and returns it with
 *   Content-Encoding: br. Clients that do not advertise Brotli support receive the
 *   uncompressed .html from the static asset bucket. No dynamic compression runs at
 *   the edge; this step does all compression once at build time.
 *
 * Uses the core compression utility _helpers/compression.js for the actual Brotli call.
 */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; brotliCompress&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BROTLI_LEVEL&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./compression.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Recursively find all .html files under a directory.
 *
 * Used to discover every HTML file in _site (root index.html, blog posts, portfolio
 * items, 404.html, etc.). Paths are returned as relative to _site so that we can
 * join them with siteDir for full paths and still have readable relative paths for
 * logging and error messages.
 *
 * @param {string} dir - Absolute or relative directory path to search (e.g. ./_site or a subdir).
 * @param {string[]} [acc=[]] - Accumulator array; results are pushed here during recursion.
 * @returns {string[]} Relative paths to .html files (e.g. [&#39;index.html&#39;, &#39;blog/post/index.html&#39;]).
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findHtmlFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; acc &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entries &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;withFileTypes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entry &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; entries&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fullPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fullPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token function&quot;&gt;findHtmlFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fullPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; acc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; entry&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			acc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;relPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; acc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Compress all HTML files in _site with Brotli level 11, writing .br files.
 *
 * Call this only after the Eleventy build has completed and _site is fully written.
 * For each .html file: reads content, compresses with brotliCompress (level 11),
 * writes &amp;lt;filename&gt;.br next to the original. Existing .br files are skipped if their
 * mtime is &gt;= the source .html mtime (incremental safety; in a full build all HTML is
 * usually newer, so most files are compressed). Logs counts, total bytes saved, and
 * duration; collects per-file errors without stopping the loop.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;compressHtmlFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🚀 PRODUCTION POSTBUILD: Starting HTML Brotli compression&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; siteDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;siteDir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;⚠️  _site directory not found, skipping HTML compression&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findHtmlFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;siteDir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;⚠️  No HTML files found, skipping compression&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; compressedCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; skippedCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; totalOriginal &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; totalCompressed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; relPath &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; htmlFiles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;siteDir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; relPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;inputPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.br&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Skip writing if .br already exists and is not older than the source .html&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputStats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputStats &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;statSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputStats&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mtime &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; inputStats&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mtime&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					skippedCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fileContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;brotliCompress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileContent&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BROTLI_LEVEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compressedSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			compressedCount&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			totalOriginal &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; originalSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			totalCompressed &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; compressedSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;❌ &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;relPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; totalTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; startTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; savedPercent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; totalOriginal &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; totalCompressed &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; totalOriginal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;0&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;✅ Compressed &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;compressedCount&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; HTML file(s) (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;skippedCount&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; skipped, up-to-date)&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compressedCount &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;   &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalOriginal &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; KB → &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalCompressed &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; KB (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;savedPercent&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;% smaller)&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;   Finished in &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;totalTime&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;ms (&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;totalTime &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toFixed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;s)&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;nErrors:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;e&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with minimal comments and much improved readability is available in a &lt;a href=&quot;https://gist.github.com/Nooshu/568f44fe571d11660bc3e4e281f6329b&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gist here&lt;/a&gt;.&lt;h3 id=&quot;cloudflare-pages-function-for-content-negotiation&quot;&gt;Cloudflare Pages Function for content negotiation&lt;/h3&gt;&lt;p&gt;Here we have the Cloudflare Function file that runs in the Cloudflare Worker. It is located in the &lt;code&gt;/functions&lt;/code&gt; directory in the root of the repository so it can be detected when Cloudflare Pages builds.&lt;pre class=&quot;language-js&quot; data-gist=&quot;5176f0d3446e05629501130912f09570&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * Catch-all Cloudflare Pages Function: HTML document content negotiation.
 *
 * This Function runs for every GET and HEAD request that is not handled by a more
 * specific route (e.g. POST /api/contact is handled by functions/api/contact.js).
 * Its job is to serve pre-compressed Brotli (.br) HTML when the client supports it,
 * and otherwise pass through to the static asset bucket (uncompressed HTML).
 *
 * Pre-compressed .br files are produced at build time by _helpers/html-compression.js
 * (postbuild): every .html in _site gets a matching .br (e.g. index.html.br,
 * blog/post/index.html.br). This Function does not compress on the fly; it only
 * chooses which asset to serve and sets the correct response headers.
 *
 * Flow:
 * 1. If the request is not for an HTML document URL (see below), pass through to ASSETS.
 * 2. If the client does not send Accept-Encoding: br, pass through (serve uncompressed HTML).
 * 3. Otherwise, fetch the corresponding .br asset; if missing or error, fall back to ASSETS
 *    for the original request (uncompressed).
 * 4. If .br exists, return it with Content-Encoding: br and no-transform so nothing
 *    re-compresses the body.
 */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleHtmlWithBrotli&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; url &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; pathname &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pathname&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Only treat as HTML document: root (/), directory-style paths ending in /, or 404&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// (Cloudflare Pages maps /404 to /404.html). All other paths (e.g. /script.js,&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// /style.css, /favicon.ico) go straight to static assets; Pages serves .br for&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// static files when available and client sends Accept-Encoding: br.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isHtmlDocument &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; pathname&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/404.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;isHtmlDocument&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ASSETS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; acceptsBrotli &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Accept-Encoding&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;acceptsBrotli&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ASSETS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Map URL path to the .br file in the asset bucket. Eleventy outputs directory&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// indexes as index.html (e.g. /blog/post/ → blog/post/index.html), so the .br&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// file is &amp;lt;path&gt;index.html.br.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
		pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/index.html.br&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; pathname &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/404.html&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/404.html.br&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;pathname&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;index.html.br&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brUrl &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;origin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ASSETS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brUrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;brResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ASSETS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Build response from the .br body with headers that declare Brotli and prevent&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// any downstream re-compression or transformation.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;headers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Encoding&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/html; charset=UTF-8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Vary&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Accept-Encoding&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// no-transform: tells caches (including Cloudflare) not to re-encode or modify the body&lt;/span&gt;
	headers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Cache-Control&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;public, max-age=31536000, no-transform&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; brResponse&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// encodeBody: &#39;manual&#39; — we are returning the body as-is (already Brotli).&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Without this, the Workers runtime might try to compress the response again.&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;encodeBody&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;manual&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function function-variable&quot;&gt;onRequestGet&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleHtmlWithBrotli&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function function-variable&quot;&gt;onRequestHead&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleHtmlWithBrotli&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with minimal comments and much better readability is available in a &lt;a href=&quot;https://gist.github.com/Nooshu/5176f0d3446e05629501130912f09570&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gist here&lt;/a&gt;.&lt;h3 id=&quot;build-lifecycle-wiring&quot;&gt;Build lifecycle wiring&lt;/h3&gt;&lt;p&gt;This is the main build file I use to build my 11ty blog for production on Cloudflare Pages.&lt;pre class=&quot;language-js&quot; data-gist=&quot;a6299b047d6f88a586de9b126216b49e&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * Build lifecycle wiring: registers Eleventy before/after hooks for production.
 *
 * This module is the single place where postbuild steps are scheduled. It is loaded
 * from the main Eleventy config (e.g. eleventy.config.js), which calls
 * registerBuildEvents(eleventyConfig). Handlers run only when env.isLocal is false
 * (i.e. production or preview builds); local dev builds skip all of this so that
 * _site is left as plain output and the dev server stays fast.
 *
 * Order of operations:
 * - eleventy.before: clear CSS build cache so CSS is regenerated and Brotli-compressed
 *   from scratch when needed.
 * - eleventy.after: run the production postbuild phase in a fixed order:
 *   1. generatePreloadHeaders() — write Link headers into _headers for CSS (and .br).
 *   2. minifyJavaScriptFiles()   — minify JS in _site/js (must run before Brotli).
 *   3. compressJavaScriptFiles() — Brotli-compress JS to .br.
 *   4. compressHtmlFiles()       — Brotli-compress all HTML in _site to .br.
 *   Then: destroy HTTP/HTTPS global agents to avoid hanging connections, and on
 *   production only force process.exit(0) after a short delay so the process
 *   terminates cleanly on CI/Cloudflare Pages.
 */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; env &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_data/env.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; clearCssBuildCache &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_helpers/css-manipulation.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; generatePreloadHeaders &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_helpers/header-generator.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; compressHtmlFiles &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_helpers/html-compression.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; compressJavaScriptFiles &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_helpers/js-compression.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; minifyJavaScriptFiles &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;../_helpers/js-minify.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Register eleventy.before and eleventy.after handlers.
 * Only registered when !env.isLocal (production/preview).
 * @param {import(&quot;@11ty/eleventy&quot;).UserConfig} eleventyConfig
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;registerBuildEvents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLocal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Before build: clear any cached CSS build artifacts so this run produces fresh&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// processed and Brotli-compressed CSS when templates reference CSS.&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eleventy.before&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;clearCssBuildCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eleventy.after&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n═══════════════════════════════════════════════════════════════════════════════&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🚀 PRODUCTION POSTBUILD PHASE: Beginning postbuild operations&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;═══════════════════════════════════════════════════════════════════════════════&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;generatePreloadHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;minifyJavaScriptFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;compressJavaScriptFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;compressHtmlFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n═══════════════════════════════════════════════════════════════════════════════&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;✅ PRODUCTION POSTBUILD PHASE: All postbuild operations completed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;═══════════════════════════════════════════════════════════════════════════════&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Tear down Node&#39;s default HTTP/HTTPS agents so the process can exit without&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// waiting for keep-alive connections to time out (e.g. on Cloudflare Pages CI).&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🔧 Forcing cleanup of HTTP connections and timers...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; http &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;http&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; https &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;globalAgent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;globalAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;✅ Destroyed HTTP global agent&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;globalAgent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;globalAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;✅ Destroyed HTTPS global agent&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;⚠️  Could not destroy HTTP agents:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;resolve&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;resolve&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;✅ HTTP connection cleanup completed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// On production builds only, force exit after a short delay so the runner&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// (e.g. Cloudflare Pages) gets a clean exit code and doesn&#39;t hang.&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isProd&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🏁 Production build complete - forcing process exit in 2 seconds...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;👋 Forcing clean exit now&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A version with minimal comments and much improved readability is available in a &lt;a href=&quot;https://gist.github.com/Nooshu/a6299b047d6f88a586de9b126216b49e&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gist here&lt;/a&gt;.&lt;h2 id=&quot;other-technical-details-worth-mentioning&quot;&gt;Other Technical details worth mentioning&lt;/h2&gt;&lt;p&gt;Here are 4 other small technical details worth explaining as part of the implementation:&lt;h3 id=&quot;why-encodebody-manual-is-required&quot;&gt;Why encodeBody: &#39;manual&#39; is required&lt;/h3&gt;&lt;p&gt;The content we send back to the user&#39;s browser is already compressed using Brotli. It is being loaded from a file that has already been compressed in advance.&lt;p&gt;If we don&#39;t tell Cloudflare to leave it alone, it may assume the content is not compressed and try to compress it again. Compressing something that is already compressed can cause problems and may result in a broken or unreadable output, plus it is a waste of CPU time and resources.&lt;p&gt;By setting &lt;code&gt;encodeBody: ‘manual&#39;&lt;/code&gt;, we are telling Cloudflare to send the content exactly as it is, without changing it. This ensures the pre-compressed file is delivered correctly to the user&#39;s browser.&lt;h3 id=&quot;why-vary-accept-encoding-and-cache-control-no-transform-matter&quot;&gt;Why Vary: Accept-Encoding and Cache-Control: no-transform matter&lt;/h3&gt;&lt;p&gt;These 2 response headers are critical for making the pre-compression work. I&#39;ve given details as to why that is below:&lt;p&gt;&lt;strong&gt;&lt;code&gt;Vary: Accept-Encoding&lt;/code&gt;&lt;/strong&gt;: This notifies browsers and CDNs that the response can change depending on what kind of compression the browser supports.&lt;p&gt;For example, if a browser says it supports Brotli, the cache will store and return the Brotli version for those requests. If another browser doesn&#39;t support Brotli, the cache will store and return a different version, such as an uncompressed version. This prevents the wrong format being sent to the wrong browser.&lt;p&gt;&lt;strong&gt;&lt;code&gt;Cache-Control: no-transform&lt;/code&gt;&lt;/strong&gt;: This informs caches and other systems between the server and the user&#39;s browser not to modify the content. It asserts that the response should not be compressed again or altered in any way.&lt;p&gt;Without this setting, a proxy might try to compress the content again, which can cause errors and waste processing power. With this header in place, the already compressed file is stored and delivered exactly as intended.&lt;h3 id=&quot;incremental-build-optimisation-runtime-check-to-skip-unchanged-files&quot;&gt;Incremental build optimisation (runtime check to skip unchanged files)&lt;/h3&gt;&lt;p&gt;After the Eleventy build finishes, the post-build step recursively scans through the output folder, such as the &lt;code&gt;_site&lt;/code&gt; directory, and checks each HTML file.&lt;p&gt;Before compressing a file, it checks whether a matching &lt;code&gt;.br&lt;/code&gt; file already exists and whether it is up-to-date. If the &lt;code&gt;.br&lt;/code&gt; file is the same age or newer than the original file, it is skipped. If the page is new or has been updated, a fresh compressed version is created.&lt;p&gt;This avoids pages that have not changed being needlessly recompressed, keeping the post build step fast. When only a few pages are updated, the need for recompression is limited.&lt;h3 id=&quot;why-we-need-a-cloudflare-function-instead-of-just-the-headers-file-for-html-brotli&quot;&gt;Why we need a Cloudflare Function instead of just the &lt;code&gt;_headers&lt;/code&gt; file for HTML Brotli?&lt;/h3&gt;&lt;h4 id=&quot;1-headers-can-only-change-headers-not-the-file-itself&quot;&gt;1. &lt;code&gt;_headers&lt;/code&gt; can only change headers, not the file itself&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;_headers&lt;/code&gt; file lets us add or modify (but not remove) response headers. It doesn&#39;t control which file is actually sent back to the browser.&lt;p&gt;So, when someone visits &lt;code&gt;/&lt;/code&gt; or &lt;code&gt;/blog/post/&lt;/code&gt;, Cloudflare Pages automatically serves &lt;code&gt;index.html&lt;/code&gt; or &lt;code&gt;blog/post/index.html&lt;/code&gt;.&lt;p&gt;If we want to serve the Brotli version of the HTML, we need to send &lt;code&gt;index.html.br&lt;/code&gt; instead. But the &lt;code&gt;_headers&lt;/code&gt; file has no way to switch the file being served.&lt;h4 id=&quot;2-setting-the-header-alone-is-not-enough&quot;&gt;2. Setting the header alone is not enough&lt;/h4&gt;&lt;p&gt;Even if you add &lt;code&gt;Content-Encoding: br&lt;/code&gt; in the static &lt;code&gt;_headers&lt;/code&gt; file, the actual file being sent would still be the normal uncompressed version of the HTML.&lt;p&gt;The browser would see this Brotli header and try to decompress the response. Since the content being sent isn&#39;t compressed, it would simply fail and the page would break.&lt;h2 id=&quot;results&quot;&gt;Results&lt;/h2&gt;&lt;p&gt;Looking at my build logs I can now see that compressing the HTML to Brotli 11 has had the following results:&lt;blockquote&gt;&lt;p&gt;📊 HTML Brotli 11 total savings: 123.4 KB (75.2% reduction)&lt;/blockquote&gt;&lt;p&gt;That’s not too bad a saving considering how simple it is to set up and integrate into the 11ty build process! Thankfully, now that it’s done I can just “set it and forget it!”. Let&#39;s examine the results from the DevTools Network panel below:&lt;h3 id=&quot;devtools-before&quot;&gt;DevTools Before&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-300.webp 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-600.webp 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-814.webp 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-1200.webp 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-1250.webp 1250w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The Firefox DevTools panel before implementation, no Brotli compression in the Content-Encoding response header Network panel column&quot; decoding=&quot;async&quot; height=&quot;524&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-300.png 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-600.png 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-814.png 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-1200.png 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/NnpcUW5tX8-1250.png 1250w&quot; width=&quot;1250&quot;&gt;&lt;/picture&gt;&lt;h3 id=&quot;devtools-after&quot;&gt;DevTools After&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-300.webp 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-600.webp 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-814.webp 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-1200.webp 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-1250.webp 1250w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The Firefox DevTools panel before implementation, Brotli compression can be seen in the Content-Encoding response header Network panel column&quot; decoding=&quot;async&quot; height=&quot;524&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-300.png 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-600.png 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-814.png 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-1200.png 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/16NWb1-HYF-1250.png 1250w&quot; width=&quot;1250&quot;&gt;&lt;/picture&gt;&lt;h3 id=&quot;devtools-after-page-reload&quot;&gt;DevTools After Page Reload&lt;/h3&gt;&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-300.webp 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-600.webp 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-814.webp 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-1200.webp 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-1250.webp 1250w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The Firefox DevTools panel on page reload, no Brotli compression in the Content-Encoding response header Network panel column can be seen&quot; decoding=&quot;async&quot; height=&quot;514&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-300.png&quot; srcset=&quot;https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-300.png 300w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-600.png 600w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-814.png 814w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-1200.png 1200w, https://nooshu.com/blog/2026/02/21/precompressed-html-at-the-edge-eleventy-meets-cloudflare-workers/VCyv4pYajj-1250.png 1250w&quot; width=&quot;1250&quot;&gt;&lt;/picture&gt;&lt;p&gt;Curiously, when reloading the page with DevTools open, the HTTP status code changes from 200 to 304 and the Brotli compression in the &lt;code&gt;Content-Encoding: br&lt;/code&gt; disappears. The reason for this is because either:&lt;ol&gt;&lt;li&gt;The reload request doesn’t contain an &lt;code&gt;Accept-Encoding: br&lt;/code&gt; header so the Cloudflare Worker is simply returning the uncompressed version of the HTML file as is expected.&lt;li&gt;The 304 has no response body, so there’s nothing to show as being compressed.&lt;/ol&gt;&lt;h2 id=&quot;difference-in-build-times&quot;&gt;Difference in build times&lt;/h2&gt;&lt;p&gt;Thanks to &lt;a href=&quot;https://toot.cafe/@tomayac&quot;&gt;Thomas Steiner on Mastodon&lt;/a&gt; for the reminder. It completely slipped my mind to share the difference in build time before and after adding HTML Brotli compression.&lt;ul&gt;&lt;li&gt;Total Build time before: ~ 2 minutes 58 seconds&lt;li&gt;Total Build time after: ~ 3 minutes 16 seconds&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Compressed&lt;/strong&gt;: 485 HTML file(s) (0 skipped, up-to-date) &lt;strong&gt;Total time&lt;/strong&gt;: 18.191299 seconds (about 18.19s) to Brotli compress all HTML files.&lt;/blockquote&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;By pre-compressing this blog’s HTML during the 11ty build phase, I have reduced the number of bytes sent on each page load. While the savings are small for a low traffic site like mine, at scale across millions of users and billions of requests per day, this approach could deliver meaningful bandwidth reductions and incremental performance improvements. This is especially true where network speed and stability vary globally.&lt;p&gt;Thank you for reading, I hope you found it useful. &lt;a href=&quot;https://en.wikipedia.org/wiki/Edge_computing&quot;&gt;Edge Workers&lt;/a&gt; are an incredibly powerful technology. I genuinely look forward to using them again in the future.&lt;p&gt;I always open to feedback and corrections. If you spot anything that needs fixing or is incorrect, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;. I will credit you in the post changelog below.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;21/02/26: Initial post published.&lt;li&gt;24/02/26: Thanks to &lt;a href=&quot;https://toot.cafe/@tomayac&quot;&gt;Thomas Steiner&lt;/a&gt; for nudging me to add details about the difference in build times!&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Asset fingerprinting and the preload response header in 11ty</title>
    <link href="https://nooshu.com/blog/2025/09/02/asset-fingerprinting-and-the-preload-response-header-in-11ty/" />
    <updated>2025-09-02T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/09/02/asset-fingerprinting-and-the-preload-response-header-in-11ty/</id>
    <content type="html">&lt;p&gt;This blog post will be building on a number of blog posts that I wrote earlier in the year. These were the posts:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/&quot;&gt;Using an 11ty Shortcode to craft a custom CSS pipeline&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/05/cranking-brotli-up-to-11-with-cloudflare-pro-and-11ty/&quot;&gt;Cranking Brotli up to 11 with Cloudflare Pro and 11ty&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/23/the-speed-trifecta-11ty-brotli-11-and-css-fingerprinting/&quot;&gt;The Speed Trifecta: 11ty, Brotli 11, and CSS Fingerprinting&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;Some insights from my earlier posts may carry over here, so check them out for overlap or a fuller view of my custom CSS pipeline for 11ty.&lt;p&gt;In this post, I’ll improve performance by adding the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel/preload&quot;&gt;preload technique&lt;/a&gt; to my blog. First, let’s look at what it is.&lt;h2 id=&quot;preload-basics&quot;&gt;Preload basics&lt;/h2&gt;&lt;p&gt;In a standard web page load, once requested, the server sends over the HTML document as well as numerous response headers too. The HTML is progressively served to the browser, and it is &lt;strong&gt;only&lt;/strong&gt; when the parser encounters the standard &lt;code&gt;&amp;lt;link src=&quot;example.css&quot; rel=&quot;stylesheet&quot;&gt;&lt;/code&gt; link that the browser requests the CSS file from the server. Therefore, best practice is to place this CSS link as close to the top of the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; tag as you can. This ensures that the browser sees it quickly and thus starts downloading it as soon as it can. But what if you could give the browser a &quot;hint&quot; as to what is coming up in the document? This is where the Preload hint functionality comes in.&lt;p&gt;The Preload hint is essentially saying to the browser:&lt;blockquote&gt;&lt;p&gt;I know you&#39;re busy doing other things at the moment, but you should also know that you absolutly &lt;strong&gt;will&lt;/strong&gt; be requiring this file soon in the page load. So stick it at the top of your list to download as soon as you can.&lt;/blockquote&gt;&lt;p&gt;It&#39;s important to realise that this is only a &quot;hint&quot;, not a mandatory instruction. The browser may choose to entirely ignore it, if for example it has already parsed and discovered the file you wish for it to preload. There are 2 ways in which you can implement a preload.&lt;h3 id=&quot;1-link-in-the-head&quot;&gt;1. Link in the head&lt;/h3&gt;&lt;p&gt;This is probably the easiest way to add a preload to a website. Stick it in the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt;:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;style.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;preload&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;main.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;script&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;2-preload-link-header&quot;&gt;2. Preload link header&lt;/h3&gt;&lt;p&gt;This method ensures that the browser is told about what other resources to load along with the HTML document in the form of a response header from the server. The above &quot;Link in the head&quot; functionality looks like this as a response header:&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;Link: &amp;lt;/style.css&gt;; rel=preload; as=style
Link: &amp;lt;/main.js&gt;; rel=preload; as=script&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;Link: &amp;lt;/style.css&gt;; rel=preload; as=style, &amp;lt;/main.js&gt;; rel=preload; as=script&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Both headers give the exact same functionality, it just comes down to readability. I don&#39;t believe the single line version give any performance advantages, especially when any form of header compression is applied, e.g. &lt;a href=&quot;https://httpwg.org/specs/rfc7541.html&quot;&gt;hpack&lt;/a&gt; for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTP_2&quot;&gt;HTTP/2&lt;/a&gt; or &lt;a href=&quot;https://datatracker.ietf.org/doc/rfc9204/&quot;&gt;qpack&lt;/a&gt; for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTP_3&quot;&gt;HTTP/3&lt;/a&gt;. But please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt; if this assumption I&#39;m making isn&#39;t true!&lt;p&gt;In both instances above, we are telling the browser to preload the page’s CSS and JavaScript because they &lt;strong&gt;will&lt;/strong&gt; be required to render the page. You may notice that I phrased it as “will be required”. This is deliberate because it is far too easy to abuse the preload functionality. If you instruct the browser to preload everything, you will likely harm web performance instead of improving it. So &lt;strong&gt;only&lt;/strong&gt; preload assets that are genuinely needed for the page to render. Otherwise, you risk wasting bandwidth on unnecessary resources and slowing down the rendering process. I know Firefox warns you in the DevTools console if an asset has been preloaded but not used during a certain time period, other browsers may do this as well. So always check your browser console for similar messages.&lt;h2 id=&quot;preload-and-fingerprinting&quot;&gt;Preload and fingerprinting&lt;/h2&gt;&lt;p&gt;There is a small added challenge when using asset fingerprinting with the preload functionality. Since the filename of the CSS or JavaScript changes completely whenever the file contents change, you cannot simply preload &lt;code&gt;index.css&lt;/code&gt; or &lt;code&gt;main.js&lt;/code&gt;. They will instead be renamed to something like &lt;code&gt;index-362ccd3816.css&lt;/code&gt; or &lt;code&gt;main-2fc0e9cad0.js&lt;/code&gt;. These are just example names, but the important point is that the file names are unpredictable and will change with each build, assuming the content of the files change. Since nobody wants to update a preload reference by hand every time a file changes, this is where a bit of 11ty scripting magic steps in to save the day.&lt;h2 id=&quot;the-code&quot;&gt;The Code&lt;/h2&gt;&lt;p&gt;In order to roll this functionality into my 11ty build, I created a helper file in my &lt;code&gt;_helpers&lt;/code&gt; directory in the root of my blog. This is called &lt;code&gt;header-generator.js&lt;/code&gt;. Imaginative name, huh! All functionality related to the header generation will be contained within this file.&lt;p&gt;It is then imported into my &lt;code&gt;eleventy.config.js&lt;/code&gt; like so:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; generatePreloadHeaders &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./_helpers/header-generator.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now, I only want this code to run in production, and after the 11ty build completes, so I added the following later in the config:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;IsProduction&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;eleventy.after&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; generatePreloadHeaders&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hopefully, this code is fairly self-explanatory. I&#39;m hooking into the &lt;code&gt;eleventy.after&lt;/code&gt; event, which is the point at which my CSS has been Brotli compressed and fingerprinted, and the Link Header is ready to be generated and added to my Cloudflare Pages &lt;code&gt;_headers&lt;/code&gt; file (&lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/headers/&quot;&gt;documentation here&lt;/a&gt;) before the &lt;code&gt;_site&lt;/code&gt; is built. Below is the complete &lt;code&gt;header-generator.js&lt;/code&gt; file I am using, with detailed comments to make it easier to follow:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;235ef1c57213479cd305f88f3389f6cf&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// standard node library imports&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This script generates preload headers for fingerprinted CSS files in the _site/css directory&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// and adds them to the global section of the _headers file (/*) in the _site directory.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// It prefers Brotli-compressed files (e.g. *.css.br rather than *.css) if available.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generatePreloadHeaders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Log the start of the process&lt;/span&gt;
	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Generating preload headers for CSS files...&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// This is my CSS directory for my blog&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssDir &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Check if CSS directory exists&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssDir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;CSS directory not found, skipping header generation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Find fingerprinted CSS files (both .css and .css.br). We prefer .css.br if available.&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Fingerprinted files match the pattern index-[hash].css or index-[hash].css.br&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssFiles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssDir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Match files like index-b9fcfe85ef.css.br or index-b9fcfe85ef.css&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;^index-[a-f0-9]{10}&#92;.css(&#92;.br)?$&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Nothing found so exit&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;No fingerprinted CSS files found, skipping header generation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Sort to prefer *.br files over *.css files&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// (compression is done via the zlib library in another helper file)&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// both .css and .css.br files exist in the same folder with the same file hash&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// The hash is generated from the unminified and uncompressed CSS file)&lt;/span&gt;
	cssFiles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;a&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If a is .br and b is not, a comes first&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If b is .br and a is not, b comes first&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.br&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Take the first (preferably .br) file&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssFiles&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Construct the path for the Link header&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/css/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssFile&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Found CSS file: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssFile&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Now we need to read the existing _headers file, add the preload header to the global section (/*),&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// and write it back&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Read the source headers file&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sourceHeadersPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;_headers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Set our target headers file&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; targetHeadersPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;_headers&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Check if source _headers file exists&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceHeadersPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Source _headers file not found&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Read the existing headers content&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; headersContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sourceHeadersPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Create the preload header&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Note: &#39;nopush&#39; prevents Cloudflare from doing HTTP/2 server push&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; preloadHeader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;  Link: &amp;lt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&gt;; rel=preload; as=style; nopush&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Find the global /* rule and add the preload header to it&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Look for the line that just contains &quot;/*&quot; which is the global section&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; lines &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; headersContent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; nextSectionIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Find the global section (line that starts with just &quot;/*&quot;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/*&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Find the next section (line that starts with a path)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;  &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					nextSectionIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// If we found the global section, proceed to add or update the Link header&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Check if a Link header already exists in the global section&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; linkHeaderExists &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Iterate through the lines in the global section&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextSectionIndex &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; nextSectionIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Check for the existance of the Link header&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Link:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// Replace existing Link header&lt;/span&gt;
					lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; preloadHeader&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// The Link header exists and has been updated&lt;/span&gt;
					linkHeaderExists &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// If no Link header exists, add one&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;linkHeaderExists&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Find the last header line in the global section&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; lastHeaderIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; globalSectionIndex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Iterate until the next section or end of file&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; globalSectionIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;nextSectionIndex &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; nextSectionIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;&amp;&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;i&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;  &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						lastHeaderIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; i&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Insert the Link header after the last header&lt;/span&gt;
				lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;splice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastHeaderIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; preloadHeader&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// rejoin the modified _headers file&lt;/span&gt;
			headersContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lines&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Could not find global section in _headers file&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Write the updated headers to the _site directory before deployment to Cloudflare pages&lt;/span&gt;
		fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;targetHeadersPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headersContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Generated preload header: Link: &amp;lt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&gt;; rel=preload; as=style; nopush&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Error generating preload headers:&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For a cleaner version without comments, I’ve uploaded the code to a Gist. &lt;a href=&quot;https://gist.github.com/Nooshu/235ef1c57213479cd305f88f3389f6cf&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Find the code Gist here&lt;/a&gt;.&lt;h2 id=&quot;the-cloudflare-headers-file&quot;&gt;The Cloudflare &lt;code&gt;_headers&lt;/code&gt; file&lt;/h2&gt;&lt;p&gt;This setup is currently working really well, the only minor &quot;issue&quot; that doesn&#39;t sit right with me presently is the fact that the Link header sits on the global header path (&lt;code&gt;/*&lt;/code&gt;) in the &lt;code&gt;_headers&lt;/code&gt; file. This means the Link header is added to &lt;strong&gt;all assets&lt;/strong&gt; served from my blog.&lt;p&gt;As far as I know, this shouldn&#39;t cause any issues, as browsers will just ignore it if on a file type that doesn&#39;t support it. But I would like to rectify this in the future.&lt;p&gt;In my testing with the Cloudflare &lt;code&gt;_headers&lt;/code&gt; file, once a header is set in Cloudflare Pages it cannot be removed or overwritten. The only &quot;fixes&quot; I’ve found for this are:&lt;ol&gt;&lt;li&gt;Use a response header transform rule in the Cloudflare dashboard to remove the Link header from all other file types served except CSS.&lt;li&gt;Look into a Cloudflare Workers solution to examine the server responses at &quot;the edge&quot; and remove them that way.&lt;/ol&gt;&lt;p&gt;I will eventually move forward with option 1 as it looks to be the most straight-forward way to do it.&lt;p&gt;I&#39;ve mentioned this &quot;minor issue&quot; in the blog post, simply to highlight the fact about not being able to remove headers using the &lt;code&gt;_headers&lt;/code&gt; file once they have been set. If anyone knows how to do this using &lt;strong&gt;only&lt;/strong&gt; the &lt;code&gt;_headers&lt;/code&gt; file, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;. I’d love to learn how.&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;All this is now live on this very blog, so using my custom CSS pipeline with 11ty, I now have the following happening before deployment to live:&lt;ol&gt;&lt;li&gt;CSS minified and Brotli compressed to level 11 (highest).&lt;li&gt;CSS Asset fingerprinting to allow for long life &lt;code&gt;cache-control&lt;/code&gt; headers, including &lt;code&gt;immutable&lt;/code&gt;.&lt;li&gt;Preloading of the CSS file to reduce the discovery time and improve page performance.&lt;/ol&gt;&lt;p&gt;On a side note: This is probably one of the fastest (and shortest) blog posts I&#39;ve written in a while! I knew I could do it! 🤣 I hope you found it as enjoyable to read as I did to write. As always, feedback and post corrections are welcome. Spot anything wrong? Please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;02/09/25: Initial post published.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Hack to the Future - Frontend</title>
    <link href="https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/" />
    <updated>2025-08-26T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/</id>
    <content type="html">&lt;h2 id=&quot;table-of-contents&quot;&gt;Table of Contents&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#hack-to-the-future-frontend&quot;&gt;Hack to the Future - Frontend&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#1-introduction&quot;&gt;1. Introduction&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#context&quot;&gt;Context&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#looking-back-at-legacy-practices&quot;&gt;Looking back at &quot;legacy&quot; practices&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#lessons-we-can-apply-today&quot;&gt;Lessons we can apply today&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#2-setting-the-time-circuits-to-the-late-90s&quot;&gt;2. Setting the Time Circuits to the late 90s&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#my-first-website-build&quot;&gt;My first website build&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#the-late-90s-web-landscape&quot;&gt;The late 90s web landscape&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#3-the-early-web-layout-and-design-practices&quot;&gt;3. The Early Web - Layout and Design Practices&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#photoshop-psds-as-the-single-source-of-truth&quot;&gt;Photoshop PSDs as the single source of truth&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#frame-based-layouts&quot;&gt;Frame-Based Layouts&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#table-based-layouts&quot;&gt;Table-Based Layouts&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#quirks-mode-layouts&quot;&gt;Quirks Mode Layouts&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#fixed-width-fonts-for-responsive-text&quot;&gt;Fixed Width Fonts for Responsive Text&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#4-the-plugin-era-flash-and-friends&quot;&gt;4. The Plugin Era – Flash and Friends&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#flash-based-content&quot;&gt;Flash-based content&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#scalable-inman-flash-replacement-sifr&quot;&gt;Scalable Inman Flash Replacement (sIFR)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#cufon&quot;&gt;Cufón&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#gif-text-replacements&quot;&gt;GIF Text Replacements&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#adobe-air&quot;&gt;Adobe AIR&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#yahoo-pipes&quot;&gt;Yahoo Pipes&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#phonegap-apache-cordova&quot;&gt;PhoneGap / Apache Cordova&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#microsoft-silverlight&quot;&gt;Microsoft Silverlight&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#java-applets&quot;&gt;Java Applets&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#5-the-javascript-framework-explosion&quot;&gt;5. The JavaScript Library Explosion&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#dhtml-beginnings-1997&quot;&gt;DHTML Beginnings (1997)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#prototype-js-2005&quot;&gt;Prototype.js (2005)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#script-aculo-us-2005&quot;&gt;Script.aculo.us (2005)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#dojo-toolkit-2005&quot;&gt;Dojo Toolkit (2005)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#yahoo-user-interface-yui-2006&quot;&gt;Yahoo! User Interface (YUI) (2006)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#moo-fx-2005&quot;&gt;moo.fx (2005))&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#mootools-2006&quot;&gt;MooTools (2006)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#jquery-core-2006&quot;&gt;jQuery (core) (2006)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#ext-js-2007&quot;&gt;Ext.js (2007)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#jquery-ui-2007&quot;&gt;jQuery UI (2007)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#angularjs-2010&quot;&gt;AngularJS (2010)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#backbone-2010&quot;&gt;Backbone (2010)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#knockout-2010&quot;&gt;Knockout (2010)&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#6-css-workarounds-and-browser-quirks&quot;&gt;6. CSS Workarounds and Browser Quirks&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#old-css-practices&quot;&gt;Old CSS practices&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#sliding-doors-technique&quot;&gt;Sliding Doors Technique&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#image-sprites-for-icons&quot;&gt;Image Sprites for Icons&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#vendor-prefixes-for-css&quot;&gt;Vendor Prefixes for CSS&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#heavy-use-of-important-in-css&quot;&gt;Heavy Use of !important in CSS&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#oldie-hacks&quot;&gt;OldIE hacks&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#doctype-fragility&quot;&gt;DOCTYPE fragility&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#zoom-1-hack&quot;&gt;zoom: 1 hack&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#underscore-hack&quot;&gt;Underscore Hack&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#asterisk-hack&quot;&gt;Asterisk Hack&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#star-html-hack&quot;&gt;Star HTML Hack&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#child-selector-hack&quot;&gt;Child Selector hack&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#double-margin-float-bug&quot;&gt;Double Margin Float Bug&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#peekaboo-bug-fix&quot;&gt;Peekaboo bug fix&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#transparent-png-fix&quot;&gt;Transparent PNG fix&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#lack-of-ie-developer-tools&quot;&gt;Lack of IE Developer Tools&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#ie-conditional-comments&quot;&gt;IE Conditional Comments&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#ie-css-selector-limit&quot;&gt;IE CSS Selector Limit&lt;/a&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#7-markup-of-the-past&quot;&gt;7. Markup of the Past&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#xhtml-1-1-and-2-0&quot;&gt;XHTML 1.1 and 2.0&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#inline-javascript&quot;&gt;Inline JavaScript&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#document-write&quot;&gt;Document.write()&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#fixed-viewport-meta-tags&quot;&gt;Fixed Viewport Meta Tags&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#web-safe-fonts-only-before-font-face&quot;&gt;Web Safe Fonts Only (before @font-face)&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#8-tools-and-workflow-relics&quot;&gt;8. Tools and Workflow Relics&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#svn-subversion-largely-replaced-by-git&quot;&gt;SVN (subversion, largely replaced by Git)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#chrome-frame&quot;&gt;Chrome Frame&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#css-resets&quot;&gt;CSS Resets&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#hover-only-interactions&quot;&gt;Hover-Only Interactions&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#9-legacy-web-strategies&quot;&gt;9. Legacy Web Strategies&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#blackhat-seo&quot;&gt;Blackhat SEO&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#above-the-fold-obsession&quot;&gt;&quot;Above the Fold&quot; obsession&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#superseded-compatibility-approaches&quot;&gt;Superseded compatibility approaches&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modernizr&quot;&gt;Modernizr&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#10-tests-and-standards-of-yesteryear&quot;&gt;10. Tests and Standards of Yesteryear&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#acid2-and-acid3-tests&quot;&gt;Acid2 and Acid3 Tests&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;11. What Still Matters - Progressive Enhancement&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#not-legacy-but-often-forgotten&quot;&gt;Not legacy but often forgotten&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#what-is-progressive-enhancement&quot;&gt;What is Progressive Enhancement&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#html&quot;&gt;HTML&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#css&quot;&gt;CSS&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#javascript&quot;&gt;JavaScript&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#progressive-enhancement-summary&quot;&gt;Progressive Enhancement Summary&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#importance-in-government-services&quot;&gt;Importance in government services&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#12-lessons-for-the-future&quot;&gt;12. Lessons for the Future&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#what-these-legacy-practices-teach-us-today&quot;&gt;What these legacy practices teach us today&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#applying-lessons-to-modern-frontend-work&quot;&gt;Applying lessons to modern frontend work&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#post-summary&quot;&gt;13. Post Summary&lt;/a&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;h2 id=&quot;1-introduction&quot;&gt;1. Introduction&lt;/h2&gt;&lt;h3 id=&quot;context&quot;&gt;Context&lt;/h3&gt;&lt;p&gt;So over the last few months at work, I&#39;ve been conducting interviews to hire Frontend Developers for a number of new projects we have in the pipeline. It was only when looking at CV&#39;s that it struck me, a lot of these candidates weren&#39;t even born when I first started in my Web Development career! So I thought maybe developers getting into a Frontend Developer career today, may want to learn a bit about what it was like when I first started (that sentence just makes me feel old! 👴)&lt;h3 id=&quot;looking-back-at-legacy-practices&quot;&gt;Looking back at “legacy” practices&lt;/h3&gt;&lt;p&gt;Why would we want to look back on legacy best practices on the web? Other than the obvious academic and for general interest reasons?&lt;p&gt;Studying past best practices and legacy systems is crucial for understanding the evolution of technology and making informed decisions today. By examining the problems old practices were designed to solve, we gain a deeper appreciation for current best practices and avoid repeating past mistakes. As the philosopher &lt;a href=&quot;https://en.wikipedia.org/wiki/George_Santayana&quot;&gt;George Santayana&lt;/a&gt; once said: &lt;blockquote&gt;&lt;p&gt;Those who cannot remember the past are condemned to repeat it.&lt;/blockquote&gt;&lt;p&gt;This historical perspective also reveals enduring principles like &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;progressive enhancement&lt;/a&gt;, which remains vital for creating accessible and resilient systems on the web.&lt;h3 id=&quot;lessons-we-can-apply-today&quot;&gt;Lessons we can apply today&lt;/h3&gt;&lt;p&gt;For developers, understanding past methodologies is essential for properly maintaining and modernising existing systems in the future without causing critical failures. This historical knowledge will ultimately help them navigate the complexities of older codebases, to ensure they make informed decisions about how to update or replace components. Above all, reflecting on the past can help us come up with creative new ideas and prevent us from blindly following new trends. This perspective also provides a comprehensive view of how the web has evolved, grounding our current practices in a deeper understanding of the technology&#39;s history.&lt;p&gt;This process of building on past knowledge is a fundamental aspect of human progress. Just as civilizations learn from historical events to avoid repeating mistakes, developers can learn from the successes and failures of past technological eras. It&#39;s how humanity has always evolved. By building upon the accumulated wisdom and experience of those who came before us. By studying the mistakes and triumphs of the past, we improve our own work and contribute to the continuous cycle of innovation and learning that drives our entire industry forward.&lt;h2 id=&quot;2-setting-the-time-circuits-to-the-late-90s&quot;&gt;2. Setting the Time Circuits to the late 90s&lt;/h2&gt;&lt;h3 id=&quot;my-first-website-build&quot;&gt;My first website build&lt;/h3&gt;&lt;p&gt;In 1998, while working toward my &lt;a href=&quot;https://en.wikipedia.org/wiki/GCSE&quot;&gt;GCSEs&lt;/a&gt;, I became interested in art and design, this was partly thanks to having an art teacher as my form tutor throughout secondary school. That influence, combined with the opportunity to take a double art GCSE for the same effort as a single GCSE, made the choice a pretty easy one! GCSE Art, here I come!&lt;p&gt;At the same time, I was already immersed in the emerging world of the internet, spending many hours online discovering a passion for many areas of computing and online gaming thanks to &lt;a href=&quot;https://en.wikipedia.org/wiki/Team_Fortress_Classic#Development&quot;&gt;QuakeWorld Team Fortress&lt;/a&gt;, despite the frustration it caused at home by tying up the phone line all hours of the day, oh how I loved my &lt;a href=&quot;https://support.usr.com/support/5668d/5668d-ug/main.htm&quot;&gt;US Robotics 56K modem&lt;/a&gt;, with its 120-150 ping! &lt;a href=&quot;https://en.wikipedia.org/wiki/ISDN&quot;&gt;Integrated Services Digital Network (ISDN)&lt;/a&gt; or any form of broadband was still many years away for most people!&lt;p&gt;I was never exactly blessed with traditional artistic talent, painting, drawing, all of those art forms just wasn’t my thing. But I spotted an opportunity to combine my love of technology with the art curriculum. Back then, there were only about 2.4 million websites in existence worldwide. Most businesses and schools (including mine), were firmly offline. So, I proposed building a website for my final art project. To my surprise, my art teacher was absolutely thrilled with the idea. It turned out to be a first for the school and, as I later discovered, a first for the entire exam board too. Shock horror: I was ahead of the curve once. The curve has been safely ahead of me ever since.&lt;p&gt;I ended up creating a website for a fake record label, complete with a dreadful album cover, fictional artist, and made-up discography. Honestly, I wish I still had it! It was gloriously awful! I don’t recall much, but I remember the site used a &lt;code&gt;&amp;lt;frameset&gt;&lt;/code&gt; with three &lt;code&gt;&amp;lt;frame&gt;&lt;/code&gt; elements. The top frame displayed the logo, the left frame held the navigation menu, and the main frame was used for the page content.&lt;p&gt;The logo, by the way, was crafted in a program called 3D Text Studio (or something similar to that) that churned out spectacularly cheesy animated text &lt;a href=&quot;https://text.media.giphy.com/v1/media/giphy.gif?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJwcm9kLTIwMjAtMDQtMjIiLCJzdHlsZSI6Im5ld2Jvcm4iLCJ0ZXh0IjoiTm9vc2h1LmNvbSIsImlhdCI6MTc0NjMwMTg0OH0.DUY2gb7c0wa8PVD1IvRYCVKUVTxKf_OlqNeSnpF_4ro&quot;&gt;like this&lt;/a&gt;! From a web performance perspective, that single GIF exceeded 2 MB. On a 56K modem, which was the standard connection for most users of the web at the time, that translates to a 6-minute loading time for just that GIF! Fortunately, it was never hosted online and was presented to the examiners directly from my local machine.&lt;p&gt;Long story short… the examiners loved my little website and I got a double A* Art GCSE for my effort!&lt;p&gt;So what&#39;s all this preamble leading too? Well, this is just a long-winded way to tell you (again) that I&#39;m old… 😭&lt;h3 id=&quot;the-late-90s-web-landscape&quot;&gt;The late 90s web landscape&lt;/h3&gt;&lt;p&gt;There have been some things I&#39;ve noticed while questioning candidates in interviews recently, many candidates don&#39;t have the faintest idea of some old methodologies used in the world of Frontend, especially during the &quot;unstable&quot; periods of the web like the late 90s and early 00s:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Browser_wars#First_browser_war_(1995%E2%80%932001)&quot;&gt;first browser war (1995–2001): Internet Explorer vs Netscape Navigator&lt;/a&gt;.&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Browser_wars#Second_browser_war_(2004%E2%80%932017)&quot;&gt;second browser war (2004–2017): Internet Explorer vs Firefox vs Google Chrome&lt;/a&gt;.&lt;/ul&gt;&lt;p&gt;Being a Frontend Developer in the late 90s was both fun in terms of innovation, but also exceedingly stressful due to the instability of the web platform! A prime example being cross-browser development. What worked in Netscape, often looked very broken in Internet Explorer (and vice versa)! And if you had clients who were looking for &quot;pixel perfect&quot; designs across all browsers, you were in for a bad time!&lt;p&gt;Throughout this period, a plethora of methodologies, tools, and workarounds were developed to address deficiencies in the web platform. And that’s what the rest of this post will delve into. Buckle up folks, we are about to time travel to an era when the internet started with the screeching of dial-up noises and I still had brown hair!&lt;h2 id=&quot;3-the-early-web-layout-and-design-practices&quot;&gt;3. The Early Web – Layout and Design Practices&lt;/h2&gt;&lt;h3 id=&quot;photoshop-psds-as-the-single-source-of-truth&quot;&gt;Photoshop PSDs as the “single source of truth”&lt;/h3&gt;&lt;p&gt;Using Adobe Photoshop Documents (PSD) as a single source of design truth was a very common practice in the early days of web design. This was particularly common when design and development teams were siloed. A designer would create a PSD file that was intended to be precisely what the website would look like in the browser.&lt;h4 id=&quot;issues&quot;&gt;Issues&lt;/h4&gt;&lt;p&gt;There were no considerations made for page structure, behaviour, and interactions. These fixed layout PSD&#39;s encouraged bad practices like:&lt;ul&gt;&lt;li&gt;Fixed page dimensions e.g. 1024px x 768px as a static canvas.&lt;li&gt;1:1 mapping of Photoshop file to web page, which was rarely achievable, especially given cross-browser inconsistencies with page rendering.&lt;li&gt;Lack of fluid or responsive design. I realise responsive design wasn&#39;t &quot;a thing&quot; at this time, but could it have been adopted sooner if fixed-width PSD workflows hadn&#39;t ever taken hold?&lt;li&gt;The technique was more suited to static layouts, like print design, rather than web design.&lt;li&gt;There were issues tracking interaction states like anchors with hover, active, disabled, and focus.&lt;li&gt;Dynamic content was difficult to visualise (e.g., the rendering of different lengths of text in the browser).&lt;li&gt;Poor accessibility adaptations, (e.g., increased font sizes, high-contrast modes weren’t considered in the design files).&lt;/ul&gt;&lt;p&gt;The only way to solve many of these issues would be to create multiple PSD&#39;s to hold all these different design assumptions. And in doing so, file management and design revisions would quickly become impractical and prone to being incomplete or inconsistent.&lt;h4 id=&quot;broken-team-collaboration&quot;&gt;Broken team collaboration&lt;/h4&gt;&lt;p&gt;The use of PSD&#39;s as the single source of truth broke how teams could collaborate and innovate. This was because:&lt;ul&gt;&lt;li&gt;Developers would often have to interpret or translate the PSD design manually without the help of designers (e.g. due to siloed teams and strict job roles).&lt;li&gt;Changes in the design required round-trips to designers, rather than being evolved collaboratively in code.&lt;li&gt;Small team bottlenecks were common e.g. all design or development decisions needed to go through individuals rather than a whole team.&lt;li&gt;Files became outdated rapidly leading to teams working on outdated designs without realising it.&lt;li&gt;Designers often came up with designs that simply couldn&#39;t be built with the web technologies that existed at the time, especially when their designs were expected to work across different browsers.&lt;/ul&gt;&lt;h4 id=&quot;modern-alternatives&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;I&#39;d like to think that designers using Photoshop for modern web design is a thing of the past, given the vast number of tools and techniques that are way more suited to the job than Photoshop ever was. Modern teams typically use:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://m3.material.io/foundations/design-tokens/overview&quot;&gt;Design tokens&lt;/a&gt; and internal component libraries as the &quot;single source of truth&quot;.&lt;li&gt;&lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt; or similar tools with structured, token-aware components.&lt;li&gt;&lt;a href=&quot;https://webdesignerdepot.com/how-to-create-a-living-style-guide/&quot;&gt;Living style guides&lt;/a&gt; and code-driven prototypes (e.g., &lt;a href=&quot;https://storybook.js.org/&quot;&gt;Storybook&lt;/a&gt;).&lt;li&gt;Clear handoffs between teams using tools like &lt;a href=&quot;https://zeroheight.com/&quot;&gt;zeroheight&lt;/a&gt;, or integrated design-to-dev platforms.&lt;/ul&gt;&lt;p&gt;The advantage of using these modern collaboration tools enables design and development teams to share the same language and source of truth, rooted in reusable, well-tested, and accessible components.&lt;h4 id=&quot;photoshop-psds-summary&quot;&gt;Photoshop PSDs Summary&lt;/h4&gt;&lt;p&gt;In the early days of my frontend career, slicing PSDs was second nature, but that workflow is now obsolete. Using Photoshop as a &quot;single source of truth&quot; leads to siloed teams, rigid layouts, and poor collaboration. It ignores responsiveness, accessibility, and the realities of modern web development. Today, tools like &lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt;, &lt;a href=&quot;https://design-system.service.gov.uk/&quot;&gt;design systems&lt;/a&gt;, and &lt;a href=&quot;https://open-wc.org/guides/community/component-libraries/&quot;&gt;component libraries&lt;/a&gt; enable faster, more inclusive, and collaborative workflows. If you’re still building from PSDs, it’s time to move on! As the web has evolved, it is imperative that we all do the same.&lt;h3 id=&quot;frame-based-layouts&quot;&gt;Frame-Based Layouts&lt;/h3&gt;&lt;p&gt;The Frame-based layouts were introduced into browsers to solve a specific set of problems. These were:&lt;ul&gt;&lt;li&gt;To allow static content like navigation menus to remain in place while only the main content of the page gets updated on navigation.&lt;li&gt;To Reduce the amount of data transferred over the network, since only one part of the page would need to be loaded. This was important at the time as remember in the late 1990s and early 2000s, broadband for most people simply wasn&#39;t available. If you were very lucky (and had the money), you&#39;d be able to get an Integrated Services Digital Network (ISDN) line installed in your home, but it was mostly online businesses that had the money (and justification) for this type of connection, even ISDN wasn’t particularly quick. Adjusted for inflation you&#39;d be looking at £60 to £80 per month for a &lt;strong&gt;0.128 Mbps&lt;/strong&gt; connection!&lt;li&gt;To simulate a more app-like experience before JavaScript (JS) and CSS became more standardised and mature.&lt;/ul&gt;&lt;h4 id=&quot;example&quot;&gt;Example&lt;/h4&gt;&lt;p&gt;For those curious here&#39;s an simple example of an HTML page using frames:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Frameset//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/frameset.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Simple Frame Example&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Note no Body element: 2 vertical columns --&gt;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 30% width the menu.html document --&gt;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 70% width for the main content of the page --&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frameset&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;30%,70%&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frame&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;menu.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;menuFrame&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frame&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;contentFrame&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;frameset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Notes&lt;/strong&gt;:&lt;ol&gt;&lt;li&gt;To use &lt;code&gt;&amp;lt;frame&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;frameset&gt;&lt;/code&gt; you needed to use a specific HTML 4.01 &lt;strong&gt;Frameset&lt;/strong&gt; DOCTYPE, in the index.html file.&lt;li&gt;In my example, for a single HTML page you&#39;d have to maintain 3 HTML files (index.html, menu.html, and content.html).&lt;li&gt;Each frame was like a mini browser window that loaded its own HTML document.&lt;/ol&gt;&lt;h4 id=&quot;problems&quot;&gt;Problems&lt;/h4&gt;&lt;p&gt;Unfortunately, there were a number of major issues with Frame-Based Layouts:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Terrible user experience&lt;/strong&gt;: the use and navigation of frames was confusing for users, since you effectively had multiple browser panes in a single page. The URL bar would often remain static even as the content of the page changed.&lt;li&gt;&lt;strong&gt;Poor Accessibility&lt;/strong&gt;: Screen readers and other assistive technology struggled to navigate frames, making it incredibly difficult for users with disabilities to understand the page content and overall page structure.&lt;li&gt;&lt;strong&gt;Limited Search Engine Optimisation (SEO) compatibility&lt;/strong&gt;: Even Search engines of the day struggled to understand the index pages built within frames. This lead to poor visibility in search results, as crawlers frequently failed to understand the relationship between the different frames.&lt;li&gt;&lt;strong&gt;Navigation and Browser Compatibility&lt;/strong&gt;: Because the back and forward buttons did not consistently produce the desired results, frames disrupted the navigation history, making it difficult for users to find their way around. The fact that different browser vendors weren&#39;t aligned with how frames should work in browsers lead to cross-browser issues too.&lt;li&gt;&lt;strong&gt;Bad for security&lt;/strong&gt;: Frames allowed for security risks like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking&quot;&gt;clickjacking&lt;/a&gt;. This is where an attacker gets a user to interact with a page that contains malicious content without the user even realising. Modern browsers now include &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Frame-Options&quot;&gt;protections&lt;/a&gt; to stop these types of security issues.&lt;/ol&gt;&lt;h4 id=&quot;modern-alternatives-2&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Modern CSS Layouts&lt;/strong&gt;: Flexbox and Grid allow for responsive layouts without compromising navigation, accessibility, and SEO.&lt;li&gt;&lt;strong&gt;Single Page Applications (SPAs)&lt;/strong&gt;: Frameworks like React, Angular, and Vue allow developers to load page content dynamically without the need for full-page reloads. Be careful though, these libraries come with their own inherent issues if not used correctly!&lt;li&gt;&lt;strong&gt;Server-Side Rendering and Partial Updates&lt;/strong&gt;: techniques like &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Side_Includes&quot;&gt;server-side includes&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/AJAX&quot;&gt;AJAX&lt;/a&gt;, or component-based rendering to update portions of a page efficiently.&lt;/ul&gt;&lt;h4 id=&quot;frame-based-summary&quot;&gt;Frame-Based Summary&lt;/h4&gt;&lt;p&gt;As mentioned in the introduction at the start of this post, my first website was built using frames! I sincerely hope you never have to maintain a frame-based website! But given the enormity of the internet, it is almost certain websites exist somewhere out there, having been untouched for decades! If you do come across one remember to take a quick peek at the source code, it&#39;s like looking back in time! They once served a purpose in the early days of the web but are now considered obsolete. Their usage introduced more problems than they solved, and have been replaced with techniques that are more performant, accessible, and maintainable. Any modern website should be using semantic HTML, CSS-based layouts, and &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;progressive enhancement&lt;/a&gt;.&lt;h3 id=&quot;table-based-layouts&quot;&gt;Table-Based Layouts&lt;/h3&gt;&lt;p&gt;In the late 1990s and early 2000s table-based layouts were a common technique for building a web page structure: A simple example of what this would look like is below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Table Layout Example&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;!-&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;Very&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;simple&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;CSS&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;modify&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;elements&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;table-based&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;layout&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
    &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1px solid #ccc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.header&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f2f2f2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.nav&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #e0e0e0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ffffff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;vertical-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; top&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token selector&quot;&gt;.footer&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #f2f2f2&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;text-align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.9em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;table&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100%&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cellspacing&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cellpadding&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Header --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;colspan&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      My Table-Based Web Page
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Body --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Navigation --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;nav&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Home&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;About&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Contact&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Main Content --&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Welcome&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This layout uses an HTML table for structure, which was common before CSS-based layouts became standard.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Footer --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;td&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;colspan&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;footer&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token entity named-entity&quot; title=&quot;©&quot;&gt;&amp;ampcopy;&lt;/span&gt; 2025 Example Company
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;why-was-it-used&quot;&gt;Why was it used?&lt;/h4&gt;&lt;p&gt;At the time CSS and layout techniques were inconsistent and unstable across browsers. Developers looking for stability in cross-browser rendering turned to tables in order to do this. At the time, tables offered:&lt;ul&gt;&lt;li&gt;Predictable cross-browser rendering&lt;li&gt;Control over alignment, spacing, and sizing&lt;li&gt;Ability to nest elements in a grid-like structure&lt;/ul&gt;&lt;p&gt;It was very common to see nested tables and transparent &quot;spacer GIFs&quot; in invisible table cells to control these layouts more precisely. You&#39;d often find logo&#39;s, sidebars, navigations, footer, and content areas all laid out within a deeply nested HTML table in order to achieve the layout and design that was required.&lt;h4 id=&quot;why-was-it-so-bad&quot;&gt;Why was it so bad?&lt;/h4&gt;&lt;p&gt;The first and hopefully most obvious point is that the &lt;code&gt;&amp;lt;table&gt;&amp;lt;/table&gt;&lt;/code&gt; element was intended for the display of &lt;strong&gt;tabular data&lt;/strong&gt;. The fact that it was used as a workaround for the lack of standardised layout techniques, shows the ingenuity of developers at the time.&lt;p&gt;Unfortunately, the use of tables for layout came with many considerable downsides, these included:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Semantics&lt;/strong&gt;: As mentioned, tables should represent structured data, not layout. Misusing them confuses assistive technologies and harms accessibility.&lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: Table-based layouts are challenging to read, modify, or scale. Small changes often require restructuring entire layouts.&lt;li&gt;&lt;strong&gt;Responsiveness&lt;/strong&gt;: They are rigid and not suited to fluid or responsive design, that was to come a number of years later.&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: They delay rendering because browsers need to calculate the entire table layout before painting it to the page.&lt;/ul&gt;&lt;h4 id=&quot;is-the-technique-still-used&quot;&gt;Is the technique still used?&lt;/h4&gt;&lt;p&gt;There are some areas where table-based layouts may still be seen:&lt;ol&gt;&lt;li&gt;Legacy code bases that desperately need to be refactored, I can imagine there are many internal systems across the world where table-based layouts are still used. I’d imagine the conversation about modernising goes something like this… &quot;If it still works, why change it?&quot;. Very short-sighted I know!&lt;li&gt;Table-based layouts are still widely used in emails due to the very limited support for CSS in email clients. It&#39;s not always the lack of support, it&#39;s the fact that many clients simply strip out any CSS in the process of rendering the email HTML.&lt;ul&gt;&lt;li&gt;To give you an example of how bad it still is, from Outlook 2007+, Microsoft switched to &lt;strong&gt;Microsoft Word&lt;/strong&gt; as the HTML rendering engine! And it&#39;s still in use today with Outlook 365! I did my fair share of HTML emails as a Junior Frontend Developer, the internationalised versions were the worst! Using the same table-based layouts for 19+ languages is never going to work well, especially with languages like German with their huge word length! Sorry… rant over!&lt;/ul&gt;&lt;li&gt;They are often still used in PDF generation tools e.g. data-driven print views: invoices etc.&lt;/ol&gt;&lt;h4 id=&quot;modern-alternatives-3&quot;&gt;Modern alternatives&lt;/h4&gt;&lt;p&gt;Modern CSS offers clean, semantic, and powerful layout tools, including:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout&quot;&gt;Flexbox&lt;/a&gt;&lt;/strong&gt;: One-dimensional layouts (ideal for nav bars, toolbars, etc.)&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout&quot;&gt;CSS Grid&lt;/a&gt;&lt;/strong&gt;: Two-dimensional layouts (ideal for full-page layout and complex structures)&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries&quot;&gt;Media Queries&lt;/a&gt;&lt;/strong&gt;: Enable responsiveness across devices&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries&quot;&gt;Container Queries&lt;/a&gt;&lt;/strong&gt; (still an emerging technology): Context-aware layout changes.&lt;/ul&gt;&lt;h4 id=&quot;table-based-summary&quot;&gt;Table-Based Summary&lt;/h4&gt;&lt;p&gt;Table-based layouts are a throwback to a bygone era, thankfully! The years of building HTML emails has scared me for life! As they were developed during a period in which CSS was inadequate for the task. Developers had to get creative to wrestle with browser quirks, and tables were the go-to workaround. Thankfully, these days, we’ve moved on to semantic HTML and proper CSS that actually does what we need (for webpages anyway). It’s cleaner, more flexible, and maintainable, and way better for accessibility.&lt;h3 id=&quot;quirks-mode-layouts&quot;&gt;Quirks Mode Layouts&lt;/h3&gt;&lt;p&gt;This topic is covered in more detail later in the blog post, but I’ll briefly mention it here for completeness.&lt;p&gt;It&#39;s important to realise that Quirks Mode Layouts weren&#39;t only limited to Internet Explorer (IE). It &lt;strong&gt;originated&lt;/strong&gt; with Internet Explorer, but it was &lt;strong&gt;not exclusive&lt;/strong&gt; to IE. Not only that, it later became a cross-browser convention in order to preserve the compatibility with many web pages on the internet. As that&#39;s the primary rule to consider when rolling out any new technology changes on the web. Whatever you do, &quot;&lt;strong&gt;don&#39;t break the web&lt;/strong&gt;!&quot;.&lt;p&gt;For example, if a vendor released a new browser feature that wasn&#39;t backwards compatible with earlier versions of web pages, then you have a major issue as you&#39;ve just broken the web! I talk about &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#what-did-xhtml-2-0-aim-to-achieve&quot;&gt;XHTML 2.0&lt;/a&gt; later in the post, as it is a prime example of a proposed technology that would have broken the web. This backwards compatibility was the sole purpose of Quirks mode. It gave modern browsers the ability to switch between:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Quirks Mode&lt;/strong&gt;: Mimic pre-standards behaviour. Used for old, non-compliant pages.&lt;li&gt;&lt;strong&gt;Standards Mode&lt;/strong&gt;: Adheres to modern web specifications (W3C and WHATWG standards).&lt;li&gt;&lt;strong&gt;Almost Standards Mode&lt;/strong&gt;: The same as Standards mode only with one exception, table cell line-height rendering. This was to preserve layouts that used inline images inside HTML tables.&lt;/ol&gt;&lt;h4 id=&quot;how-were-layouts-triggered&quot;&gt;How were layouts triggered?&lt;/h4&gt;&lt;p&gt;The browser decided which layout mode to use from the list above purely from the DOCTYPE used on the page. For example:&lt;h5 id=&quot;trigger-quirks-mode&quot;&gt;Trigger Quirks mode&lt;/h5&gt;&lt;p&gt;This DOCTYPE will trigger Quirks mode layout:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It looks valid, but it is missing the system identifier (URL) therefore it is a malformed DOCTYPE so Quirks Mode is triggered. A valid DOCTYPE is given below for comparison:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That missing URL in the DOCTYPE is vital. Quirks Mode would also be activated if a page did not have a DOCTYPE or was not identical to the valid DOCTYPE given above &lt;strong&gt;in any way&lt;/strong&gt;. IE even had a really nasty habit of triggering Quirks Mode if any character was output in the page source before the DOCTYPE. This included invisible characters and new lines and line returns too! As you can imagine, it made debugging issues an absolute nightmare!&lt;h5 id=&quot;almost-standards-mode&quot;&gt;Almost Standards Mode&lt;/h5&gt;&lt;p&gt;The following DOCTYPE&#39;s will trigger Almost Standards Mode:&lt;ol&gt;&lt;li&gt;HTML 4.01 &lt;strong&gt;Transitional&lt;/strong&gt; (with full system identifier):&lt;/ol&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/loose.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;2&quot;&gt;&lt;li&gt;HTML 4.01 &lt;strong&gt;Frameset&lt;/strong&gt; (with full system identifier):&lt;/ol&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01 Frameset//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/frameset.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;3&quot;&gt;&lt;li&gt;XHTML 1.0 &lt;strong&gt;Transitional&lt;/strong&gt;:&lt;/ol&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD XHTML 1.0 Transitional//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&quot;4&quot;&gt;&lt;li&gt;XHTML 1.0 &lt;strong&gt;Frameset&lt;/strong&gt;&lt;/ol&gt;&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD XHTML 1.0 Frameset//EN&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&quot;standards-mode&quot;&gt;Standards Mode&lt;/h5&gt;&lt;p&gt;And lastly and most importantly for modern web development. This is the DOCTYPE you should be using to trigger standards mode in all modern browsers:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This simplified DOCTYPE was brought in as part of the HTML5 Specification after 6 years of standardisation (2008–2014).&lt;h6 id=&quot;why-was-this-version-created&quot;&gt;Why was this version created?&lt;/h6&gt;&lt;p&gt;As outlined in all the examples above, previous DOCTYPE versions were:&lt;ul&gt;&lt;li&gt;Long&lt;li&gt;Error-prone&lt;li&gt;Required both a public and a system identifier&lt;li&gt;Affected rendering modes (Quirks, Almost Standards, Standards)&lt;/ul&gt;&lt;p&gt;In order to solve these issues this the new DOCTYPE:&lt;ul&gt;&lt;li&gt;does not reference a Document Type Definition (DTD) as (HTML5 no longer relies on &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/SGML&quot;&gt;SGML-based validation&lt;/a&gt;).&lt;li&gt;only has a single purpose: to trigger Standards Mode in all modern browsers.&lt;/ul&gt;&lt;h4 id=&quot;quirks-mode-summary&quot;&gt;Quirks Mode Summary&lt;/h4&gt;&lt;p&gt;As we have discussed above, Quirks Mode wasn&#39;t an IE exclusive layout mode. It was introduced into all browsers in order to &quot;not break the web&quot;. To ensure your website uses Standards Mode, use:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And remember it &lt;strong&gt;must&lt;/strong&gt; be the first characters in the source code on the page!&lt;h3 id=&quot;iframe-embeds-for-layouts-or-content&quot;&gt;Iframe Embeds for Layouts or Content&lt;/h3&gt;&lt;p&gt;If you&#39;ve already read the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#frame-based-layouts&quot;&gt;Frame-based Layouts&lt;/a&gt; section above, then this section will be very similar. Although both are now considered legacy techniques, they come with distinct differences.&lt;h4 id=&quot;frameset&quot;&gt;Frameset&lt;/h4&gt;&lt;p&gt;As I discussed earlier here&#39;s example &lt;code&gt;&amp;lt;frameset&gt;&lt;/code&gt; code:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- column 1 25% width / column 2 75% width --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frameset&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;cols&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;25%,75%&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frame&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;nav.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;frame&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;main.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;frameset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;The &lt;code&gt;&amp;lt;frameset&gt;&lt;/code&gt; tag completely replaced the &lt;code&gt;&amp;lt;body&gt;&lt;/code&gt; tag and allowed developers to split the browser window into multiple, scrollable, resizable sections.&lt;li&gt;Each section (&lt;code&gt;&amp;lt;frame&gt;&lt;/code&gt;) loaded a separate HTML document (as seen in the code above).&lt;li&gt;This technique was intended to use them as a layout structure. e.g. different parts of the User interface (UI) came from different HTML documents.&lt;li&gt;Navigation in one frame would control the content in another frame.&lt;/ul&gt;&lt;h4 id=&quot;inline-frames-iframes&quot;&gt;Inline frames (Iframes)&lt;/h4&gt;&lt;p&gt;These were introduced later in the &lt;a href=&quot;https://www.w3.org/TR/html401/&quot;&gt;HTML 4.01 Transitional specification&lt;/a&gt;. Example &lt;code&gt;&amp;lt;iframe&gt;&lt;/code&gt; code is found below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;iframe&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;content.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;iframe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You will immediately notice the difference, using an &lt;code&gt;&amp;lt;iframe&gt;&lt;/code&gt; doesn&#39;t replace the &lt;code&gt;&amp;lt;body&gt;&lt;/code&gt; tag, it embeds an external HTML page into the original page.&lt;ul&gt;&lt;li&gt;The usage of iframes was to embed content into other pages, not structure the whole layout.&lt;li&gt;iframes were used to load external or isolated content within a single, self-contained HTML page.&lt;/ul&gt;&lt;h4 id=&quot;user-experience&quot;&gt;User Experience&lt;/h4&gt;&lt;p&gt;Both methods came with issues, but iframes were slightly better but still caused significant problems:&lt;ul&gt;&lt;li&gt;Embeds could be styled and resized, but remained isolated from the parent.&lt;li&gt;Navigation, SEO, and accessibility were still severely impacted when misused.&lt;/ul&gt;&lt;h4 id=&quot;standards-and-browser-support&quot;&gt;Standards and Browser Support&lt;/h4&gt;&lt;p&gt;Framesets were deprecated and completely removed from the &lt;a href=&quot;https://html.spec.whatwg.org/&quot;&gt;HTML5 specification&lt;/a&gt;, modern browsers either no longer support them or support is very limited without the correct DOCTYPE.&lt;p&gt;Iframes are still a part of the HTML5 specification, but they come with limited use cases, including:&lt;ul&gt;&lt;li&gt;Secure sandboxing&lt;li&gt;Third-party embeds&lt;/ul&gt;&lt;p&gt;In fact, the performance of iframe embeds has recently &lt;a href=&quot;https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attributes&quot;&gt;been improved&lt;/a&gt; by browsers vendors &lt;a href=&quot;https://caniuse.com/loading-lazy-attr&quot;&gt;adding support&lt;/a&gt; for &lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt; on iframes thus allowing the browser to delay loading until the iframe is in the viewport.&lt;h4 id=&quot;security&quot;&gt;Security&lt;/h4&gt;&lt;p&gt;Thankfully, iframes are more secure on the modern web thanks to attributes like:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/sandbox&quot;&gt;&lt;code&gt;sandbox&lt;/code&gt;&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/allow&quot;&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/referrerPolicy&quot;&gt;&lt;code&gt;referrerpolicy&lt;/code&gt;&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;Although the use of iframes still needs careful consideration for content integrity, cross-domain policies, and maintainability. They can still introduce security vulnerabilities such as:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Clickjacking&quot;&gt;Clickjacking&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/XSS&quot;&gt;Cross-site scripting (XSS)&lt;/a&gt; exploits.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS&quot;&gt;Cross-origin resource sharing (CORS)&lt;/a&gt; exploits.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-them&quot;&gt;Can I still use them?&lt;/h4&gt;&lt;p&gt;Yes, iframes are still a part of the HTML5 specification, so you can still use them, but you should try to only use them in specific scenarios where required. For example, when you need to embed third-party content into a website. This is often seen in banking on the modern web, when you are paying for something online. The final step of the transaction frequently loads an iframe from your bank asking to verify your purchase, usually via some form of Multi-factor Authentication (MFA) e.g. SMS, banking app approval on your phone. While iframe embeds aren&#39;t specifically required for &lt;a href=&quot;https://www.pcisecuritystandards.org/&quot;&gt;PCI compliance&lt;/a&gt;. Many online merchants tend to implement them along with &lt;a href=&quot;https://en.wikipedia.org/wiki/3-D_Secure&quot;&gt;3D Secure authentication&lt;/a&gt; to help reduce PCI scope and exposure.&lt;h4 id=&quot;modern-alternatives-4&quot;&gt;Modern alternatives&lt;/h4&gt;&lt;p&gt;As mentioned in the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#frame-based-layouts&quot;&gt;Frame-Based Layouts&lt;/a&gt; section, there are a number of modern alternatives rather than using iframes for page layout, modularisation, and code isolation. These include:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Modern CSS Layouts&lt;/strong&gt;: Flexbox and Grid allow for responsive layouts without compromising navigation, accessibility, and SEO.&lt;li&gt;&lt;strong&gt;Single Page Applications (SPAs)&lt;/strong&gt;: Frameworks like React, Angular, and Vue allow developers to load page content dynamically without the need for full-page reloads. Be careful though, these libraries come with their own inherent issues if not used correctly!&lt;li&gt;&lt;strong&gt;Server-Side Rendering and Partial Updates&lt;/strong&gt;: techniques like &lt;a href=&quot;https://en.wikipedia.org/wiki/Server_Side_Includes&quot;&gt;server-side includes&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/AJAX&quot;&gt;AJAX&lt;/a&gt;, or component-based rendering to update portions of a page efficiently.&lt;/ul&gt;&lt;h4 id=&quot;iframe-embeds-summary&quot;&gt;Iframe Embeds Summary&lt;/h4&gt;&lt;p&gt;Using iframes for modularisation and isolation is now seen as a legacy approach and should generally be avoided on the modern web. That said, they still have a place for things like third-party embeds. I remember the early days of Facebook marketing, iframes were everywhere and an absolute nightmare to work with, especially when trying to get the sizing right within their UI. Urghh!&lt;h3 id=&quot;pixel-perfect-design&quot;&gt;Pixel-Perfect Design&lt;/h3&gt;&lt;p&gt;Pixel-perfect design is a now outdated design approach that was once considered the gold-standard in frontend development. The presented UI on the web page was intended to be a pixel-perfect replica of the design mockups.… If that sounds like a ridiculous and totally unworkable strategy, then I can confirm, yes it was!&lt;h4 id=&quot;what-the-technique-means-in-practice&quot;&gt;What the technique means in practice&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Strict visual fidelity&lt;/strong&gt;: Developers were expected to reproduce every element of a design using the exact measurements, colours and fonts given in a static design file like a Photoshop Document file (PSD).&lt;li&gt;&lt;strong&gt;Close alignment with design tools&lt;/strong&gt;: The priority while using this technique was to match the layout in the design document, no matter what. Inconsistencies across various browsers, not to mention differing screen sizes and user preferences, frequently made this an impossible task.&lt;li&gt;&lt;strong&gt;Used in fixed-resolution environments&lt;/strong&gt;: The technique worked reasonably well in the era of desktop-only websites with very few fixed screen sizes (e.g. 800×600, 1024×768).&lt;/ul&gt;&lt;h4 id=&quot;why-is-it-considered-legacy&quot;&gt;Why is it considered legacy?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Responsive design is now standard&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Modern devices now range from the size of watches, all the way up to ultra-wide monitors and TV&#39;s and everything in between. Pixel perfect design fails on both smaller and larger size screens.&lt;/ul&gt;&lt;li&gt;&lt;strong&gt;Accessibility and user preferences&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Modern Accessibility-first design requires designs to be flexible in terms of layout and scaling. For example, &lt;a href=&quot;https://www.w3.org/TR/WCAG22/#resize-text&quot;&gt;WCAG 2.2 Success Criterion 1.4.4&lt;/a&gt; expects text to be scaled up to 200% without loss of content or functionality. This simply can&#39;t be done when using the pixel-perfect design technique.&lt;/ul&gt;&lt;li&gt;&lt;strong&gt;Performance and Maintainability&lt;/strong&gt;:&lt;ul&gt;&lt;li&gt;It encourages the use of hardcoded CSS rather than using scalable design systems and components.&lt;/ul&gt;&lt;li&gt;&lt;strong&gt;Modern Design Tools and Systems&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Tools like &lt;a href=&quot;https://www.figma.com/&quot;&gt;Figma&lt;/a&gt; and &lt;a href=&quot;https://www.sketch.com/&quot;&gt;Sketch&lt;/a&gt; now exist with scalable units, that can create responsive mockups rather than static visuals.&lt;/ul&gt;&lt;li&gt;&lt;strong&gt;Design Intent Over Exact Replication&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;Modern web design focuses on intent and consistency across contexts, prioritising &lt;strong&gt;usability&lt;/strong&gt; and &lt;strong&gt;accessibility&lt;/strong&gt; over pixel-perfect replication by leveraging newly introduced responsive browser technologies.&lt;/ul&gt;&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;I hate to be the bearer of bad news, but no, you can&#39;t. It simply isn&#39;t compatible with the modern web, for all the reasons I&#39;ve given above. You are better off understanding what the technique was trying to achieve and focus on modern-day alternatives.&lt;h4 id=&quot;modern-alternatives-5&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Use of &lt;a href=&quot;https://www.w3.org/community/design-tokens/&quot;&gt;Design tokens&lt;/a&gt; for spacing, colour and typography.&lt;li&gt;Modern browser layouts like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/grid&quot;&gt;CSS Grid&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout&quot;&gt;Flexbox&lt;/a&gt;.&lt;li&gt;&lt;a href=&quot;https://component.gallery/&quot;&gt;Component libraries&lt;/a&gt; and &lt;a href=&quot;https://designsystemsrepo.com/design-systems/&quot;&gt;Design Systems&lt;/a&gt; that abstract repeated patterns.&lt;li&gt;Focus on the user content, not the dimensions of the UI viewing it.&lt;/ul&gt;&lt;h4 id=&quot;pixel-perfect-summary&quot;&gt;Pixel-Perfect Summary&lt;/h4&gt;&lt;p&gt;Pixel-perfect design was always a terrible design technique that set everyone up to fail with the expectation of pixel accurate designs across browsers and devices. It served its purpose when the web was a lot simpler than it is today. A pixel-perfect design on today&#39;s web is a clear indication of an outdated aesthetic, urgently requiring a modern overhaul.&lt;h3 id=&quot;fixed-pixel-layouts&quot;&gt;Fixed Pixel Layouts&lt;/h3&gt;&lt;p&gt;There&#39;s a reason why I&#39;ve mentioned Fixed Pixel layouts directly after Pixel-Perfect Design. They are related, but have differing concepts. They both emerged in an early era of web design, but they serve different purposes.&lt;h4 id=&quot;pixel-perfect-design-2&quot;&gt;Pixel-perfect Design&lt;/h4&gt;&lt;p&gt;As you will have just read above, pixel-perfect design emphasises the precise implementation of a design on a website. The output of the website should be identical to the design, down to every pixel. For this reason, it is a &lt;strong&gt;philosophy&lt;/strong&gt;, not a &lt;strong&gt;layout strategy&lt;/strong&gt;.&lt;h4 id=&quot;fixed-pixel-layouts-2&quot;&gt;Fixed Pixel Layouts&lt;/h4&gt;&lt;p&gt;Fixed pixel layouts are a &lt;strong&gt;layout strategy&lt;/strong&gt; where all widths, heights, and positions are defined using fixed pixel values (e.g. &lt;code&gt;width: 1024px&lt;/code&gt;). This means it the page layout doesn&#39;t adapt to different screen sizes or resolutions. They are rigid and non-responsive, and are optimised for a single resolution or screen size. Because of this Fixed Pixel Layouts break on small screen sizes (like mobiles), or large displays. They are very much associated with older websites and legacy intranet tools. If you look at early websites on the web they would often say &quot;Best viewed with Internet Explorer (or Netscape Navigator) at 800×600 (or 1024×768)&quot;. These are prime examples of where Fixed Pixel layouts were used.&lt;h4 id=&quot;fixed-pixel-summary&quot;&gt;Fixed Pixel Summary&lt;/h4&gt;&lt;p&gt;Fixed pixel layouts and pixel-perfect design are outdated. They are relics from a time when monitor sizes were uniform and browser zooming was uncommon. Pixel-perfect demands you match the design mockup exactly, which usually ends in fragile CSS and breaks the moment someone dares to increase their font size or use a high contrast mode. Fixed pixel layouts take it even further by hard-coding dimensions, making sites fall apart on anything that isn’t a desktop from 2010. Adding to their drawbacks, neither option integrates well with modern accessibility standards, responsive design principles, or current web usage patterns.&lt;h3 id=&quot;css-floats-for-layout&quot;&gt;CSS Floats for Layout&lt;/h3&gt;&lt;p&gt;CSS floats were my go-to layout technique for many years, simply because there really wasn&#39;t a viable cross-browser alternative (other than &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#table-based-layouts&quot;&gt;Tables&lt;/a&gt;, which I covered earlier in the post). Floats were originally intended to be used for wrapping text around images on a page. They weren&#39;t intended to be used for full-page layouts. At the time, designers and developers did this as a creative way to make certain layouts, not because that was the design goal of the CSS specifications.&lt;h4 id=&quot;common-issues&quot;&gt;Common issues&lt;/h4&gt;&lt;p&gt;Float-based layouts came with a major issue: fragile designs often required “clearfix” hacks to stop float containers from collapsing.&lt;h5 id=&quot;example-clearfix-hacks&quot;&gt;Example clearfix hacks&lt;/h5&gt;&lt;p&gt;Here are a few example hacks that ensured the float container would wrap around the floated elements:&lt;p&gt;&lt;strong&gt;Modern (Recommended)&lt;/strong&gt;:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.clearfix::after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; table&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;clear&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; both&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;clearfix container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token language-css css value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Left&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token language-css css value&quot;&gt;&lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Right&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Overflow Hidden (Quick Fix)&lt;/strong&gt;:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This was my personal go to for &quot;clearfix&quot; as it was so simple to add to the CSS. A nasty downside of this method was it would hide any content that needed to overflow outside the float container, so its usage really depended on the design requirements.&lt;p&gt;&lt;strong&gt;Float the Container Itself (Not Recommended)&lt;/strong&gt;:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I don&#39;t remember really using this method simply because it could interfere with other layout elements on a page and could create unexpected layout shifts during page rendering.&lt;p&gt;&lt;strong&gt;Using display: flow-root (Modern, Clean Alternative)&lt;/strong&gt;:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flow-root&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is far too modern for me, it didn&#39;t exist when I was still building UI&#39;s regularly. As, it has been stable in major browsers since &lt;a href=&quot;https://caniuse.com/flow-root&quot;&gt;January 2020&lt;/a&gt;. The advantage of this method is no pseudo-elements or hacks required. This is recommended for use if you are modernising code and want the cleanest approach without adding extra markup. Other float-based issues include:&lt;ol start=&quot;2&quot;&gt;&lt;li&gt;&lt;strong&gt;Poor readability&lt;/strong&gt;: Additional markup was required just for the container clearfix hacks. This also impacted maintainability too.&lt;li&gt;&lt;strong&gt;Inconsistent behaviour&lt;/strong&gt;: Floats tended to behave differently depending on the browser in use, although not as problematic as it was, it still requires more testing to ensure cross-browser UI consistency.&lt;li&gt;&lt;strong&gt;Stacking issues&lt;/strong&gt;: Aligning text, or centring horizontally and vertically, is &lt;a href=&quot;https://css-tricks.com/centering-css-complete-guide/&quot;&gt;non-trivial&lt;/a&gt;.&lt;/ol&gt;&lt;h4 id=&quot;modern-alternatives-6&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;As with most of the topics in this section of the post, there are a list of modern alternatives available for float-based layouts.&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout&quot;&gt;Flexbox&lt;/a&gt;&lt;/strong&gt;: One-dimensional layouts (ideal for nav bars, toolbars, etc).&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout&quot;&gt;CSS Grid&lt;/a&gt;&lt;/strong&gt;: Two-dimensional layouts (ideal for full-page layout and complex structures).&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries&quot;&gt;Media Queries&lt;/a&gt;&lt;/strong&gt;: Enable responsiveness across devices&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries&quot;&gt;Container Queries&lt;/a&gt;&lt;/strong&gt; (still an emerging technology): Context-aware layout changes.&lt;/ul&gt;&lt;h4 id=&quot;performance-and-maintainability&quot;&gt;Performance and Maintainability&lt;/h4&gt;&lt;p&gt;Modern layout techniques:&lt;ul&gt;&lt;li&gt;Lead to cleaner and more maintainable code.&lt;li&gt;Reduce reliance on utility classes for clearing float-specific bugs.&lt;li&gt;Enhance responsiveness and accessibility by ensuring that the HTML code&#39;s structure is more predictable.&lt;/ul&gt;&lt;h4 id=&quot;legacy-support-considerations&quot;&gt;Legacy Support Considerations&lt;/h4&gt;&lt;p&gt;Floats still appear in legacy code, so understanding them remains important for maintenance and modernisation. However, they should be avoided in greenfield projects unless strictly necessary (I&#39;m open to suggestions if anyone has an idea as to what this necessity might be).&lt;h4 id=&quot;css-floats-summary&quot;&gt;CSS Floats Summary&lt;/h4&gt;&lt;p&gt;Using CSS floats for layout is now considered an anti-pattern. While historically important, floats have been replaced with modern layout systems like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout&quot;&gt;Flexbox&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout&quot;&gt;Grid&lt;/a&gt;, which offer cleaner, more maintainable, and more powerful solutions. In the future as the web platform evolves, newer layout techniques such as &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid&quot;&gt;CSS Subgrid&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries&quot;&gt;Container Queries&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning&quot;&gt;Anchor Positioning&lt;/a&gt; are also progressing through standardisation and will further improve layout flexibility. Avoiding floats is a key best practice when building or modernising frontend architecture.&lt;h3 id=&quot;faux-columns&quot;&gt;Faux Columns&lt;/h3&gt;&lt;p&gt;I&#39;m not sure why the French word &quot;Faux&quot; was chosen for this technique, rather than just &quot;false&quot; or &quot;fake&quot;, maybe to make it sound more appealing? Or more complex than it actually is? The term works though, as in English, it is used to describe something made to look like something else, which is precisely what this technique did.&lt;h4 id=&quot;what-is-it&quot;&gt;What is it?&lt;/h4&gt;&lt;p&gt;In the early days of CSS there was no reliable way to make two or more columns stretch to the same height when the content length in the columns varied. This was because floats or &lt;code&gt;inline-block&lt;/code&gt; elements don&#39;t align their heights, so developers looked for a workaround.&lt;h4 id=&quot;how-it-worked&quot;&gt;How it worked&lt;/h4&gt;&lt;p&gt;It was a clever workaround actually, it typically worked like this:&lt;ol&gt;&lt;li&gt;A background image was applied to the &lt;strong&gt;parent container&lt;/strong&gt; of the floated columns (that designers wanted to look to be equal in height)&lt;li&gt;This background image was often a vertical gradient or a solid block of colour. This image was then repeated (&lt;code&gt;repeat-y&lt;/code&gt;) vertically down the parent container, giving the illusion (&quot;fake&quot;) that the columns were of equal height.&lt;li&gt;As each of the inner floated elements extended due to varying content within, the container background image extended with it. Very clever, huh!&lt;/ol&gt;&lt;p&gt;The CSS for this &quot;workaround&quot; was as simple as this:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* container with the background repeat vertically */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;faux-columns.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; repeat-y&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Note the width of this column, it is important */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.left-column&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/* Use an identical margin to the width, to &quot;push&quot; the right column into place next to the left column */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.right-column&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 200px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The background image of the container would have 2 sections, one colour for the left column and another for the right column, thus tricking a users eyes into seeing the columns having equal heights!&lt;p&gt;Check out the &lt;a href=&quot;https://alistapart.com/article/fauxcolumns/&quot;&gt;Faux Columns A List Apart (ALA) article by Dan Cederholm from January 2004&lt;/a&gt;, if you are still unsure how it works, he explained it a lot better than I have!&lt;h4 id=&quot;limitations&quot;&gt;Limitations&lt;/h4&gt;&lt;p&gt;Unfortunately, as with all of these early CSS workarounds, there were limitations. The faux columns technique was no different. These limitations included:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Not adaptable&lt;/strong&gt;: it required the background image to perfectly match the layout dimensions of the container, and it&#39;s inner &quot;columns&quot;.&lt;li&gt;&lt;strong&gt;Unresponsive&lt;/strong&gt;: Exactly what the word says, this only worked for fixed layouts, fluid of more dynamic layouts simply broke the illusion.&lt;li&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;: The technique was difficult to maintain, since any layout change required editing the CSS, background image, or both.&lt;li&gt;&lt;strong&gt;Poor semantics&lt;/strong&gt;: Yes, it solved the &lt;em&gt;visual&lt;/em&gt; presentation problem, but the underlying code wasn&#39;t semantic, the additional &lt;code&gt;&amp;lt;div&gt;&lt;/code&gt;&#39;s just for layout purposes, inherently held no semantic meaning.&lt;/ul&gt;&lt;h4 id=&quot;modern-alternatives-7&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;As you would expect, there are modern alternatives that make this layout trivial:&lt;h5 id=&quot;flexbox&quot;&gt;Flexbox&lt;/h5&gt;&lt;p&gt;With Flexbox, it really is this simple:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.left-column, .right-column&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&quot;css-grid&quot;&gt;CSS Grid&lt;/h5&gt;&lt;p&gt;In CSS grid, it&#39;s even easier as all child elements in a row are explicitly defined and align by default, no extra CSS required:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1fr&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* 1fr = 1 fraction unit */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;can-i-still-use-it-2&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Errr, silly question, no not at all! Look how effortless the modern alternatives above are! Imagine how good it feels to rip out the legacy faux column code from a legacy codebase, and replace it with 1 or 2 lines of CSS!&lt;h4 id=&quot;faux-columns-summary&quot;&gt;Faux Columns Summary&lt;/h4&gt;&lt;p&gt;The Faux Columns technique was one of those clever hacks we leaned on back when CSS didn’t give designers and developers much to work with. It did the job, but it was fragile and fiddly, and you were always one layout change away from breaking it. These days, it’s more of a historical curiosity. Flexbox and Grid have long since made it obsolete, and with newer tools like Subgrid and Container Queries coming through the standards process, we’ve moved on from trickery to browser tools that are actually built for layout.&lt;h3 id=&quot;zoom-layouts-using-css-zoom-for-responsiveness&quot;&gt;Zoom Layouts (using CSS zoom for responsiveness)&lt;/h3&gt;&lt;p&gt;Back when responsive design was first emerging, a technique called Zoom Layouts emerged in order to scale whole elements within a UI. This technique emerged because responsive CSS layout techniques were very limited.&lt;h4 id=&quot;example-2&quot;&gt;Example&lt;/h4&gt;&lt;p&gt;An simple example of this is given below:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;zoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.8&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This CSS is easy to understand, it simply scales the entire &lt;code&gt;.container&lt;/code&gt; by 80%.&lt;h4 id=&quot;when-was-it-useful&quot;&gt;When was it useful?&lt;/h4&gt;&lt;p&gt;This technique was useful when you needed to shrink or enlarge an entire layout without refactoring a fixed-width design. It also came in handy when working with legacy layouts that could not adapt with fluid widths or media queries. Lastly, it was used as a workaround before widespread browser support for the &lt;code&gt;transform: scale()&lt;/code&gt; CSS property or &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#relative_length_units&quot;&gt;relative units&lt;/a&gt; like rem, em, %, vw, and vh.&lt;h4 id=&quot;why-is-it-outdated&quot;&gt;Why is it outdated?&lt;/h4&gt;&lt;p&gt;There are a number of reasons as to why the Zoom Layout technique is now outdated. These include:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;zoom&lt;/code&gt; is non-standard and inconsistent&lt;/strong&gt;: The &lt;code&gt;zoom&lt;/code&gt; property isn&#39;t a part of any official CSS specification. It is a proprietary feature initially supported by Internet Explorer, then later Chromium-based browsers. Interestingly, Firefox and Safari have never supported the &lt;code&gt;zoom&lt;/code&gt; property, making cross-browser layouts using the technique very tricky.&lt;li&gt;&lt;strong&gt;Causes Accessibility issues&lt;/strong&gt;: &lt;code&gt;zoom&lt;/code&gt; does affect the layout scaling, but it doesn&#39;t interact well with user-initiated zoom or accessibility scaling preferences. Thus, using this technique can create barriers for users with visual impalements that rely on native browser zooming or OS-level zooming tools.&lt;li&gt;&lt;strong&gt;Breaks layout semantics&lt;/strong&gt;: zoomed elements don&#39;t always reflow correctly, for example text can reflow outside its container, images can become blurry, and form elements may not align correctly when scaled.&lt;li&gt;&lt;strong&gt;Modern CSS has better solutions&lt;/strong&gt;: As with most outdated techniques in this post, modern browsers now support much better layout techniques and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#relative_length_units&quot;&gt;relative units&lt;/a&gt;, that make responsive design much more consistent and easier to maintain. These include Flexbox, CSS Grid and rem, em, %, vw, vh units. Along with Media Queries and container queries, this gives developers the ability to adapt individual elements proportionally, rather than resorting to scaling the entire UI.&lt;li&gt;&lt;strong&gt;Performance issues&lt;/strong&gt;: The use of &lt;code&gt;zoom&lt;/code&gt; can cause serious performance issues, especially on low-powered devices since the rescaling causes the browser to scale rasterised layers rather than reflow content natively, which increases UI repaint costs.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-3&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Seriously, only if you hate your users and love additional maintenance. In practical terms, using it would not be a responsible choice; avoid it. If you come across a critical legacy site using this approach, plan to refactor it with modern techniques. Build your layouts using CSS Grid or Flexbox for flexibility across breakpoints, implement fluid typography with clamp and viewport units, adopt container queries for component-level responsiveness, rely on viewport-based units for consistency, and always test with browser zoom and assistive technologies to ensure accessibility and adaptability for all users.&lt;h4 id=&quot;zoom-layouts-summary&quot;&gt;Zoom Layouts Summary&lt;/h4&gt;&lt;p&gt;Using &lt;code&gt;zoom&lt;/code&gt; for layout responsiveness is an outdated, non-standard technique that can compromise accessibility, compatibility, and performance. Modern responsive design principles provide far more robust, scalable, and accessible solutions.&lt;p&gt;If you require a transition approach for legacy systems still using Zoom Layouts, consider refactoring incrementally to CSS Grid and Flexbox combined with relative units like rem or percentages to modernise their responsiveness. Luckily for you, this isn’t the last time you’ll hear about the infamous proprietary &lt;code&gt;zoom&lt;/code&gt; property, as it makes quite a few appearances later in the blog post when we dive into those classic IE layout quirks.&lt;h3 id=&quot;nested-ems-instead-of-pixels&quot;&gt;Nested ems instead of Pixels&lt;/h3&gt;&lt;p&gt;Before the CSS &lt;code&gt;rem&lt;/code&gt; unit was added to the &lt;a href=&quot;https://www.w3.org/TR/css-values-3/&quot;&gt;CSS Values and Units Module Level 3&lt;/a&gt;, developers used the &lt;code&gt;em&lt;/code&gt; unit as a responsive strategy to avoid fixed pixel font sizes. Having used it for years I can confirm it was a real pain in the ass to work with (pardon my French!). When using an &lt;code&gt;em&lt;/code&gt; unit, both the CSS font size and spacing were sized relative to their immediate parent container.&lt;p&gt;For example, given this HTML:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Some text here&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And this CSS:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* relative to body */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.child&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.2em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* relative to .container, so compounded */&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Can you guess what the &lt;code&gt;.child&lt;/code&gt; font size of the text is in pixels?&lt;p&gt;Better get your Math(s) hat on, let&#39;s go through it!&lt;blockquote&gt;&lt;p&gt;Default body font size = 16px so 1em x 16px = 16px. The &lt;code&gt;.container&lt;/code&gt; DIV is relative to the body font size so: &lt;code&gt;.container&lt;/code&gt; font size = 1.2em x 16px = 19.2px. The &lt;code&gt;.child&lt;/code&gt; paragraph is relative to the &lt;code&gt;.container&lt;/code&gt; font size so: &lt;code&gt;.child&lt;/code&gt; font size = 1.2em x 19.2px = &lt;strong&gt;23.04px&lt;/strong&gt;&lt;/blockquote&gt;&lt;p&gt;That&#39;s right, that well-known font size &lt;strong&gt;23.04px&lt;/strong&gt;!&lt;p&gt;Now this is just a very basic example, imagine if you include &lt;code&gt;em&lt;/code&gt; units for margins and paddings too! And also layer on additional nesting! Hopefully, you are starting to realise how painful &lt;code&gt;em&lt;/code&gt; units were to use on a website, especially when the only viable alternatives were percentages (which had the same relative nesting issue and were even less intuitive to use than &lt;code&gt;em&lt;/code&gt;), or CSS keywords e.g. &lt;code&gt;font-size: small&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;large&lt;/code&gt;, &lt;code&gt;x-large&lt;/code&gt;, etc. As you can see, there weren’t a lot of viable or maintainable options in terms of responsive typography and spacing in the early responsive design era (around 2010-2013).&lt;h4 id=&quot;why-its-outdated&quot;&gt;Why it&#39;s outdated?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Complexity and unpredictability&lt;/strong&gt;: Nested ems lead to compounded calculations as we saw in the simple example I gave above, making sizing unpredictable in deeply nested components. A small change in a parent font size cascades unexpectedly and could completely obliterate your well-crafted layout.&lt;li&gt;&lt;strong&gt;Maintenance overhead&lt;/strong&gt;: Adjusting layouts or typography with nested ems quickly creates brittle CSS and significant technical debt, especially when ems are used for spacing like margins and padding.&lt;li&gt;&lt;strong&gt;Inconsistent UI scales&lt;/strong&gt;: Components may render differently in different contexts if they rely on em units, especially in large applications with diverse layout containers.&lt;/ol&gt;&lt;h4 id=&quot;modern-alternatives-8&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;You can utilise several modern options for nested &lt;code&gt;em&lt;/code&gt; units. These include:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://www.a11y-collective.com/blog/what-is-rem-in-css/&quot;&gt;rem units&lt;/a&gt; for consistent global scaling relative to the root font size&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/clamp&quot;&gt;Clamp-based&lt;/a&gt; fluid typography for responsive design, for example &lt;a href=&quot;https://utopia.fyi/&quot;&gt;Utopia.fyi&lt;/a&gt;.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties&quot;&gt;CSS custom properties&lt;/a&gt; (variables) for consistent, maintainable scales&lt;/ul&gt;&lt;h4 id=&quot;can-i-use-them-today&quot;&gt;Can I use them today?&lt;/h4&gt;&lt;p&gt;You could, but I have no idea why you would! When more viable alternatives exist today like &lt;code&gt;rem&lt;/code&gt; units for global scaling, &lt;code&gt;clamp&lt;/code&gt; for fluid typography, and CSS variables for maintainable scales, why make life harder than it needs to be??&lt;h4 id=&quot;nested-ems-summary&quot;&gt;Nested ems Summary&lt;/h4&gt;&lt;p&gt;Using nested &lt;code&gt;em&lt;/code&gt; units is outdated. It adds unnecessary complexity and unpredictability. For modern responsive design you are far better off using rems for consistent global scaling, or taking advantage of the &lt;code&gt;clamp&lt;/code&gt; CSS function if you are feeling adventurous. Lastly, you could always use modern CSS variables for more consistent and maintainable code.&lt;h3 id=&quot;setting-the-browsers-base-font-size-to-62-5&quot;&gt;Setting the browsers base font size to 62.5%&lt;/h3&gt;&lt;p&gt;As a direct follow on from the nested &lt;code&gt;em&lt;/code&gt; technique earlier in the post, there was an alternative that developers came up with to simplify the math(s) behind percentages (since they had the same &quot;relative to parent&quot; issue as &lt;code&gt;em&lt;/code&gt;&#39;s). When developers decided to use percentages for fonts, they often set the font size on the &lt;code&gt;&amp;lt;html&gt;&lt;/code&gt; element to:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 62.5%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* default font size now 10px not 16px, due to scaling making `em` units easier to work with (base-10 rather than base-16) */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.6em &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*16px*/&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2.4em &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*24px*/&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3.6em &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/*36px*/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This avoided complicated fractional calculations when using &lt;code&gt;em&lt;/code&gt; units:&lt;ul&gt;&lt;li&gt;Without the percentage: 1em = 16px → 24px = 1.5em.&lt;li&gt;With the percentage: 1em = 10px → 24px = 2.4em.&lt;/ul&gt;&lt;p&gt;You still had the problem with nested elements, but that was later fixed by using &lt;code&gt;rem&lt;/code&gt; units (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#ems_and_rems&quot;&gt;root em&lt;/a&gt;).&lt;h4 id=&quot;why-are-these-techniques-less-common-today&quot;&gt;Why are these techniques less common today?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;It overrides user defaults&lt;/strong&gt;: Some users may increase their base font size from 16px for accessibility reasons, hard-coding the base size to 62.5% undermines this user preference.&lt;li&gt;&lt;strong&gt;Modern teams work with &lt;code&gt;rem&lt;/code&gt;&lt;/strong&gt;: Most developers and teams now accept that 1rem = 16px and use &lt;a href=&quot;https://www.uxpin.com/studio/blog/what-are-design-tokens/&quot;&gt;design tokens&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties&quot;&gt;variables&lt;/a&gt;, or a spacing scale instead of forcing a base-10 (62.5% hack) mental model.&lt;li&gt;&lt;strong&gt;Simplicity from Modern tooling&lt;/strong&gt;: &lt;a href=&quot;https://www.designsystems.com/&quot;&gt;Design systems&lt;/a&gt;, &lt;a href=&quot;https://automaticcss.com/docs/utility-classes-what-why/&quot;&gt;utility classes&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_cascading_variables/Using_CSS_custom_properties&quot;&gt;CSS variables&lt;/a&gt; handle sizing scales more predictably without the 62.5% hack.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-today&quot;&gt;Can I still use it today?&lt;/h4&gt;&lt;p&gt;No, not really, mainly due to the list of reasons I&#39;ve given above. &lt;code&gt;font-size: 62.5%&lt;/code&gt; was merely a developer convenience hack to make 1em / 1rem equal 10px for easy math(s). Look at the short list of modern alternatives I have listed above instead.&lt;h4 id=&quot;base-font-size-summary&quot;&gt;Base font size Summary&lt;/h4&gt;&lt;p&gt;As mentioned above, this math(s) hack for easier font sizing is no longer required on the modern web, in fact it should be avoided due to the impact it has on users who change their base font size for accessibility reasons. Look to use one of the more modern techniques mentioned in the &quot;Why are these techniques less common today?&quot; section above.&lt;h3 id=&quot;fixed-width-fonts-for-responsive-text&quot;&gt;Fixed-Width Fonts for Responsive Text&lt;/h3&gt;&lt;p&gt;Fixed-width fonts are better known as monospaced fonts, they allocate the same horizontal spacing to each character. For example:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.mono-spaced-font&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Courier New&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;Courier&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;Lucida Sans Typewriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;Lucida Typewriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;monospace&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The example provided above demonstrates how to render text in a monospaced font and concurrently defines a monospaced font stack for most web page implementations. The reason I say &quot;most&quot; is because Windows has a 99.73% support for &lt;code&gt;Courier New&lt;/code&gt; and OSX has 95.68% support according to &lt;a href=&quot;https://www.cssfontstack.com/Courier-New&quot;&gt;CSSFontStack&lt;/a&gt;. Which is why it is listed first in the font stack, for the less than 1% of users that don’t support it, the browser will look for Courier and so on, until it gets to the end of the font stack, where it just tells the browser to use &lt;em&gt;any&lt;/em&gt; monospace font the system has available.&lt;p&gt;Historically, monospaced fonts were used for:&lt;ul&gt;&lt;li&gt;Terminal emulation.&lt;li&gt;Code editors for alignment.&lt;li&gt;Early web design, where the layout predictability was prioritised over aesthetics or responsiveness.&lt;/ul&gt;&lt;h4 id=&quot;why-was-the-technique-used-in-responsive-text&quot;&gt;Why was the technique used in responsive text?&lt;/h4&gt;&lt;p&gt;Developers and designers struggled with the web platform&#39;s limitations at the time due to a lack of suitable tools. So monospaced text was usually used for:&lt;ul&gt;&lt;li&gt;Consistent character spacing across browsers.&lt;li&gt;Easier text alignment in table-based layouts.&lt;li&gt;Simplifying calculations for layout sizing, since browser layout strategies were much less mature than they are today.&lt;/ul&gt;&lt;h4 id=&quot;why-is-the-technique-outdated&quot;&gt;Why is the technique outdated?&lt;/h4&gt;&lt;p&gt;The technique is now considered outdated for various reasons, including:&lt;ul&gt;&lt;li&gt;It limits design flexibility. Modern responsive design has moved on from fixed typography, as fluid typography is now possible, which is better served by proportional fonts that adapt visually to varying screen sizes and reading contexts.&lt;li&gt;Monospaced fonts are harder to read, especially for paragraphs or long text blocks. This requirement on the modern web is critical for accessibility-focused design.&lt;li&gt;Instead of outdated methods, modern CSS offers enhanced tools and support for contemporary layout techniques. Flexbox and CSS Grid, coupled with various typography scaling units like &lt;code&gt;rem&lt;/code&gt;, &lt;code&gt;em&lt;/code&gt;, &lt;code&gt;vw&lt;/code&gt;, &lt;code&gt;vh&lt;/code&gt;, and &lt;code&gt;clamp()&lt;/code&gt;, enable more predictable and reliable layout control.&lt;li&gt;There&#39;s no performance difference between modern proportional fonts and monospaced fonts, they both have similar browser overhead, so why choose a technique that is harder to maintain and comes with a whole host of other disadvantages?&lt;/ul&gt;&lt;h4 id=&quot;whats-a-modern-replacement&quot;&gt;What&#39;s a modern replacement?&lt;/h4&gt;&lt;p&gt;There are a number of modern alternatives, some of which we touched on above. These include the use of:&lt;ul&gt;&lt;li&gt;Fluid typography with CSS &lt;code&gt;clamp()&lt;/code&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Values_and_units#relative_length_units&quot;&gt;viewport units&lt;/a&gt; to ensure text scales responsively across devices.&lt;li&gt;Proportional fonts with &lt;a href=&quot;https://www.cssfontstack.com/&quot;&gt;font fallback stacks&lt;/a&gt; to optimise readability and layout adaptability.&lt;li&gt;Only using monospaced fonts for semantic or functional reasons, not aesthetics. Code blocks and tabular data are prime examples of where monospaced fonts should be used to enhance readability of these certain areas of a website. Adventurous designers can even transform a web UI into a retro Terminal window with these elements, though readability must be carefully considered.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-4&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;As stated repeatedly in this section of the blog post, while technically possible, it would be highly illogical to employ this technique. Given the numerous disadvantages outlined earlier in this section, utilising such an antiquated method on the modern web would be ill-advised.&lt;h4 id=&quot;fixed-width-fonts-summary&quot;&gt;Fixed-Width Fonts Summary&lt;/h4&gt;&lt;p&gt;There are so many font options available to developers and designers today. There is no way you should ever use a monospaced font for anything besides sections of code, or possibly text in a data table, depending on what type of data you are wishing to display. In both of these cases, a monospaced font can enhance readability if used correctly. &lt;h2 id=&quot;4-the-plugin-era-flash-and-friends&quot;&gt;4. The Plugin Era – Flash and Friends&lt;/h2&gt;&lt;h3 id=&quot;flash-based-content&quot;&gt;Flash-Based Content&lt;/h3&gt;&lt;p&gt;I distinctly remember having a conversation with a then colleague regarding iOS not supporting Flash content and how it was the beginning of the end of Adobe Flash (Flash) on the web. At the time he refused to believe it, but thankfully for the web, my prediction came true!&lt;h4 id=&quot;what-was-flash-based-content&quot;&gt;What was Flash-based content?&lt;/h4&gt;&lt;p&gt;Flash was a proprietary multimedia software platform developed by Adobe. It was used to:&lt;ul&gt;&lt;li&gt;Deliver animations, video, and interactive content via a plugin in the web browser.&lt;li&gt;Enable rich media applications embedded in websites.&lt;li&gt;Power early interactive interfaces on the web, this was way before the web platform matured and could support these types of interactivity natively.&lt;li&gt;I, personally, remember it for flash-based advertisements of which I created many when I was first starting out in web development!&lt;/ul&gt;&lt;h4 id=&quot;why-was-it-popular&quot;&gt;Why was it popular?&lt;/h4&gt;&lt;p&gt;Flash was hugely popular at the time due to the fact that:&lt;ul&gt;&lt;li&gt;Cross-browser multimedia support was lacking on the web platform (i.e. no native support)&lt;li&gt;Advanced vector animation support&lt;li&gt;In 2005, Flash was the sole method for streaming audio and video on the web, as exemplified by YouTube&#39;s reliance on it.&lt;li&gt;Interaction was programmed through the use of &lt;a href=&quot;https://en.wikipedia.org/wiki/ActionScript&quot;&gt;ActionScript&lt;/a&gt;. If it sounds very familiar to JS, that&#39;s because it is, as they are both based on the &lt;a href=&quot;https://ecma-international.org/publications-and-standards/standards/ecma-262/&quot;&gt;ECMAScript Standard&lt;/a&gt;. That&#39;s a massive oversimplification, but if you are curious, read all about it on &lt;a href=&quot;https://en.wikipedia.org/wiki/ECMAScript&quot;&gt;Wikipedia&lt;/a&gt;.&lt;li&gt;In the late 1990s there was a popular trend on the web of having completely pointless Flash intros that would load and play automatically before you entered a site. There are countless example of these intros &lt;a href=&quot;https://www.youtube.com/results?search_query=website+flash+intro&quot;&gt;on YouTube&lt;/a&gt; if you are interested!&lt;/ul&gt;&lt;h4 id=&quot;why-was-it-deprecated&quot;&gt;Why was it deprecated?&lt;/h4&gt;&lt;p&gt;There are many reasons as to why Flash is now deprecated. These include:&lt;ul&gt;&lt;li&gt;Flash was well-known for serious security vulnerabilities, which were often used for malicious software and browser takeovers.&lt;li&gt;Flash content often consumed significant CPU and memory, leading to poor performance and excessive battery drain on mobile devices.&lt;li&gt;As mentioned earlier, Apple refused to support Flash on iOS, citing security, performance, and stability concerns, which contributed heavily to its decline.&lt;li&gt;Adobe&#39;s proprietary Flash technology was incompatible with open web standards, hindering accessibility, interoperability, and sustainability.&lt;li&gt;Lastly, open web standards and the web platform evolved to replace Flash with native (and non-proprietary) functionality like:&lt;ul&gt;&lt;li&gt;Native &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio&quot;&gt;video and audio playback&lt;/a&gt; (&lt;code&gt;&amp;lt;video&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&gt;&lt;/code&gt; tags)&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/animation&quot;&gt;CSS animations and transitions&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/canvas&quot;&gt;Canvas&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt; for interactive graphics and games&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG&quot;&gt;SVG&lt;/a&gt; for scalable vector graphics&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;Flash met its end with the advent of modern web APIs, including HTML5, CSS3, and modern JS.&lt;p&gt;In 2017 &lt;a href=&quot;https://blog.adobe.com/en/publish/2017/07/25/adobe-flash-update&quot;&gt;Adobe announced that Flash&#39;s end of life&lt;/a&gt; would be in 2020. In December 2020 Adobe released the final update for Flash. By January 2021, major browsers disabled Flash by default and eventually blocked Flash content entirely.&lt;h4 id=&quot;can-i-still-use-it-5&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;At last! A straightforward answer to this question: No, it&#39;s impossible for you to use Flash on the modern web as Flash content in no longer supported in any modern browser. Simple! RIP Adobe (&lt;a href=&quot;https://en.wikipedia.org/wiki/Macromedia&quot;&gt;Macromedia&lt;/a&gt;) Flash 1996 to 2020. You won&#39;t be missed.&lt;h4 id=&quot;modern-alternatives-9&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;As mentioned above, there are a number of native browser-based alternatives for Flash (HTML5, CSS3, Modern JS) functionality these are: - Native &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Structuring_content/HTML_video_and_audio&quot;&gt;video and audio playback&lt;/a&gt; (&lt;code&gt;&amp;lt;video&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;audio&gt;&lt;/code&gt; tags) - &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/animation&quot;&gt;CSS animations and transitions&lt;/a&gt; - &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/canvas&quot;&gt;Canvas&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt; for interactive graphics and games - &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG&quot;&gt;SVG&lt;/a&gt; for scalable vector graphics&lt;h3 id=&quot;scalable-inman-flash-replacement-sifr&quot;&gt;Scalable Inman Flash Replacement (sIFR)&lt;/h3&gt;&lt;p&gt;Before the introduction of the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face&quot;&gt;@font-face&lt;/a&gt; at-rule, which is defined in the &lt;a href=&quot;https://drafts.csswg.org/css-fonts/&quot;&gt;CSS Fonts Module Level 4 specifications&lt;/a&gt;, Web Designers and Frontend Developers were desperately seeking to expand the limited number of cross-browser, and cross-operating system fonts that were available on the web. In order to achieve that, a number of workarounds and general ingenious browser hackery were built. From the name of this technique you may also be able to guess the answer for the &quot;Can I still use it?&quot; section!&lt;h4 id=&quot;what-is-sifr&quot;&gt;What is sIFR?&lt;/h4&gt;&lt;p&gt;Scalable Inman Flash Replacement (sIFR) was an creative technique that used JS and Flash together to replace HTML text elements with Flash-rendered text. This feature allowed developers to embed custom fonts directly within the Flash file. Consequently, they could modify the HTML text, and the Flash file would dynamically render the updated content.&lt;p&gt;This workaround was required at the time because there was very limited support for custom web fonts via the use of &lt;code&gt;@font-face&lt;/code&gt;. Surprisingly, &lt;code&gt;@font-face&lt;/code&gt; was first introduced by Microsoft in &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Explorer_4&quot;&gt;Internet Explorer 4&lt;/a&gt; in 1997, using &lt;a href=&quot;https://en.wikipedia.org/wiki/Embedded_OpenType&quot;&gt;Embedded OpenType (EOT)&lt;/a&gt; as the font format. This was proprietary to IE, so no other browsers supported it. Since there wasn&#39;t a cross-browser way to use custom fonts, alternative techniques like sIFR emerged.&lt;h4 id=&quot;sifr-popularity&quot;&gt;sIFR Popularity&lt;/h4&gt;&lt;p&gt;sIFR emerged in the early to mid-2000s, with its first public version released around 2004-2005. sIFR was widely used until around 2009-2010, especially for headings and branded typography. Its popularity grew during that period due to the technique&#39;s preservation of SEO and accessibility advantages. This was because the original HTML text remained in the Document Object Model (DOM), allowing it to still be readable by search engines and assistive technology. And once setup it was simple to update the underlying text and sIFR took care of the rest. There was also the added bonus that the text remained selectable, so could be copied and pasted when needed. It sounds like a great solution, so where did it all go wrong?&lt;h4 id=&quot;why-its-outdated-2&quot;&gt;Why it&#39;s outdated?&lt;/h4&gt;&lt;p&gt;There are several reasons why the sIFR technique is now outdated. We covered the main one in the previous &quot;Flash-Based Content&quot; technique above:&lt;ul&gt;&lt;li&gt;It relies on the Adobe Flash Player browser plugin, that is now deprecated and blocked in all major browsers due to security vulnerabilities and performance issues.&lt;li&gt;It slowed down page performance by increasing page load time, due to having to download the Flash assets.&lt;li&gt;Although it was partially accessible from an HTML perspective, it certainly wasn&#39;t perfect as it introduced accessibility and compatibility issues on devices &lt;strong&gt;without Flash support&lt;/strong&gt;.&lt;li&gt;Web standards came to the rescue. CSS3 brought with it native cross-browser support for &lt;code&gt;@font-face&lt;/code&gt; for custom fonts, without the need for any browser plugins. The new standards supported Web Open Font Format (WOFF and WOFF2) font formats which are a standardised and optimised custom font format for the delivery of fonts on the web. Basically, HTML5 and CSS working together simply removed the need for plugin-based typography workarounds.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-6&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;As I mentioned, above this is a pretty simple question to answer… No, not at all, the removal of Flash from all modern browsers used today guarantees that!&lt;h4 id=&quot;modern-alternative&quot;&gt;Modern Alternative&lt;/h4&gt;&lt;p&gt;There&#39;s only one modern alternative that should be used on the web today: &lt;code&gt;@font-face&lt;/code&gt;. An example of its usage is given below:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyCustomFont&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.eot&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* IE9 Compat Modes */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.eot?#iefix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;embedded-opentype&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* IE6-IE8 */&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Super modern browsers */&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.woff&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Modern browsers */&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.ttf&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;truetype&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Safari, Android, iOS */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thankfully, modern browsers widely support WOFF, thus, simplifying the above code:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyCustomFont&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Super modern browsers */&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.woff&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* modern browsers */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In fact, any modern web browser that supports WOFF also supports WOFF2. Therefore, the code you should use today is as follows:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyCustomFont&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
       &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;fonts/MyCustomFont.woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;woff2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In all instances above you&#39;d use the custom font like so:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.myfontclass&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyCustomFont&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* other &quot;fallback&quot; fonts here */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The browser will take care of the rest!&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: that you should always provide a font fallback in your &lt;code&gt;font-family&lt;/code&gt; property, just in case the font file fails to load, or it is accidentally deleted from your server. There is so much more to the use of &lt;code&gt;@font-face&lt;/code&gt;. If you are interested in advanced topics around its usage you should definitely check out &lt;a href=&quot;https://fediverse.zachleat.com/@zachleat&quot;&gt;Zach Leatherman&lt;/a&gt;&#39;s copious &lt;a href=&quot;https://www.zachleat.com/web/fonts/&quot;&gt;amount of work&lt;/a&gt; on the subject over the years!&lt;h3 id=&quot;cufon&quot;&gt;Cufón&lt;/h3&gt;&lt;p&gt;Much like sIFR above, Cufón was created due to the lack of options when it came to using custom fonts on the web. It was popular around the same time as sIFR (late 2000s and early 2010s). It essentially was solving the same problem, but using a different cross-browser technique. Whereas sIFR used Flash, Cufón worked like so:&lt;ol&gt;&lt;li&gt;Fonts were converted into vector graphics, and canvas (or &lt;a href=&quot;https://en.wikipedia.org/wiki/Vector_Markup_Language&quot;&gt;VML&lt;/a&gt; for older versions of IE) this was then used to render the text in place.&lt;li&gt;The JS then replaced the HTML text with the custom font rendered version of the text.&lt;li&gt;Since it was JS-based, there was no need for any plugins (like Flash).&lt;/ol&gt;&lt;h4 id=&quot;why-was-it-used-2&quot;&gt;Why was it used?&lt;/h4&gt;&lt;p&gt;As previously noted with sIFR, browser support for CSS &lt;code&gt;@font-face&lt;/code&gt; was inadequate or inconsistent at the time. Designers and developers wanted to use custom fonts for branding and stylistic reasons without users having to install a plugin or the fonts locally. Cufón was attractive because it:&lt;ul&gt;&lt;li&gt;Didn&#39;t require a plugin for it to work.&lt;li&gt;Provided near pixel-perfect rendering of the custom font.&lt;li&gt;Was easy to integrate with minimal JS setup.&lt;/ul&gt;&lt;h4 id=&quot;why-is-it-outdated-2&quot;&gt;Why is it outdated?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;Modern browsers all support &lt;code&gt;@font-face&lt;/code&gt;, a much better solution as it allows the direct use of web fonts like WOFF or WOFF2 files without the use of JS hacks.&lt;li&gt;Its usage impacted accessibility. Due to Cufón replacing text in the DOM with rendered graphics, screen readers couldn&#39;t interpret these replacements as text, thus degrading a site&#39;s accessibility.&lt;li&gt;Cufón caused web performance issues as the text replacement script was run &lt;strong&gt;after&lt;/strong&gt; page load, which increased page render time, blocked interactivity and degraded the overall performance, especially on slower devices.&lt;li&gt;Although Cufón attempted to preserve the replaced text in the DOM, the results were often inconsistent, mainly because search engine crawlers at the time had inconsistent results when parsing JS-replaced content.&lt;li&gt;Cufón didn&#39;t work with responsive design, once rendered the Cufón replaced text didn&#39;t scale correctly, unless the page was reloaded at the new page size.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-7&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Although the &lt;a href=&quot;https://cufon.shoqolate.com/generate/&quot;&gt;site is still available here&lt;/a&gt;, and the &lt;code&gt;cufon.js&lt;/code&gt; script is still available to &lt;a href=&quot;https://raw.githubusercontent.com/sorccu/cufon/master/js/cufon.js&quot;&gt;download&lt;/a&gt;. The font generator has been taken down and is no longer maintained. So to get it working you&#39;d need to jump through quite a few hoops! So, what I&#39;m really trying to say is: Yes, you can, but it &lt;strong&gt;isn&#39;t&lt;/strong&gt; worthwhile. Even the original author Simo Kinnunen says on the website:&lt;blockquote&gt;&lt;p&gt;Seriously, though you should be using standard web fonts by now.&lt;/blockquote&gt;&lt;h4 id=&quot;modern-alternatives-10&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;Rather than repeat myself, I&#39;ll refer you to the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#scalable-inman-flash-replacement-sifr&quot;&gt;same section from the sIFR methodology above&lt;/a&gt;.&lt;h3 id=&quot;gif-text-replacements&quot;&gt;GIF Text Replacements&lt;/h3&gt;&lt;p&gt;Although I am aware that this is not a plug-in, it seemed like the most appropriate section to include it in since we are discussing font replacement techniques. Of all the custom font techniques I&#39;ve listed, this one is by far the worst in my opinion. The technique was popular in the late 1990s and into the early 2000s, when there were very few other options for using custom fonts on the web.&lt;h4 id=&quot;what-is-a-gif-text-replacement&quot;&gt;What is a GIF Text Replacement?&lt;/h4&gt;&lt;p&gt;It&#39;s a self-explanatory name. In order to use a custom font, a designer would create the static asset (usually via Photoshop or similar), then the developer would cut out the text as a GIF. This image would then be used to replace the HTML text on the page with the image, in order to make it look like a custom font was in use. Example code for this technique can be seen below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Gif text replacement for a heading --&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/heading-text.gif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Welcome to Our Website&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Gif text replacement for a navigation link --&gt;&lt;/span&gt;
			&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about.html&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
				&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;images/about-link.gif&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;About Us&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
			&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Some readers may wonder why a GIF was used instead of a PNG, since both support transparency. The main reason is that Internet Explorer offered poor support for transparent PNGs and required a complicated hack to make them work, which I will explain &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#transparent-png-fix&quot;&gt;later in the post&lt;/a&gt;.&lt;h4 id=&quot;why-was-it-so-bad-2&quot;&gt;Why was it so bad?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;It was time-consuming and maintenance heavy. Should the design change, then so would all the GIFs that had to be manually cut out of the design files again.&lt;li&gt;It was bad for Accessibility. Screen readers are unable to process text embedded within GIFs or images that lack meaningful alt text. The absence or outdated nature of alt text therefore created an exclusionary experience for users with visual impairments.&lt;li&gt;It was bad for SEO. Search engines could not index text within images, thus, harming discoverability, this technique relied on developers ensuring they had accurate alt-text, which isn&#39;t always the case.&lt;li&gt;It was bad for performance. At the time of its popularity, the web was going through a transition period from HTTP/1.0 to HTTP/1.1. Although &lt;a href=&quot;https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html&quot;&gt;HTTP/1.1 was better for TCP connections than HTTP/1.0&lt;/a&gt;, these TCP connections were very expensive in terms of web performance, and each of these GIF replacements required its own TCP connection, which increased page load times.&lt;li&gt;It was terrible for responsiveness. Although the responsive web was a few years away when it was popular, the difference between images and text rendering was text&#39;s ability to scale across different devices and screen sizes. Images simply couldn&#39;t do that, leading to poor rendering and pixelation on some devices.&lt;li&gt;GIF only supports 256 colours (8-bit) and for the GIF to be transparent, one of those colours would need to be transparent. So if your text had a complex colour palette, it either wouldn&#39;t work, or just look terrible.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-8&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;No, it&#39;s as simple as that. It&#39;s a technique with so many negatives and so few positives, it should be confined to the interesting history of the web platform!&lt;h4 id=&quot;modern-alternatives-11&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;Again, rather than repeat myself, I&#39;ll refer you to the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#scalable-inman-flash-replacement-sifr&quot;&gt;same section from the sIFR methodology above&lt;/a&gt;.&lt;h3 id=&quot;adobe-air&quot;&gt;Adobe AIR&lt;/h3&gt;&lt;p&gt;I remember going to a conference around 2007 / 2008, and there was so much hype about Adobe AIR. It was going to be the &quot;next big thing&quot;, due to the fact it could enable developers to create rich desktop and mobile apps using &lt;strong&gt;only&lt;/strong&gt; web skills and technologies.&lt;h4 id=&quot;what-was-adobe-air&quot;&gt;What was Adobe AIR?&lt;/h4&gt;&lt;p&gt;The AIR in Adobe AIR stood for Adobe Integrated Runtime. It was a cross-platform runtime developed by Adobe. It allowed developers to use: HTML, JS, Adobe Flash, &lt;a href=&quot;https://flex.apache.org/&quot;&gt;Flex&lt;/a&gt;, and &lt;a href=&quot;https://en.wikipedia.org/wiki/ActionScript&quot;&gt;ActionScript&lt;/a&gt;. All combined they could run as a standalone desktop or mobile application. It supported Windows and macOS for desktop, and later Android and iOS on mobile.&lt;p&gt;Furthermore, it also enabled:&lt;ul&gt;&lt;li&gt;Running Flash-based applications outside the browser.&lt;li&gt;Enabling rich multimedia, animations, and offline capabilities.&lt;/ul&gt;&lt;h4 id=&quot;why-is-it-outdated-3&quot;&gt;Why is it outdated?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;It relied on Flash and ActionScript. With flash reaching its end of life in late 2020 due to persistent security vulnerabilities, and the momentum behind open standards like HTML5, CSS3, and JS ES6+. AIR ceased to exist due to the loss of its core technology.&lt;li&gt;A shift to modern cross-platform frameworks. The market moved towards more efficient and performant technologies like:&lt;ul&gt;&lt;li&gt;React Native&lt;li&gt;Flutter&lt;li&gt;Electron (for desktop apps) The advantage of these frameworks is they use native components or JS runtimes, without the heavy reliance on Flash. This offered developers and users greater performance, maintainability, security, and community support.&lt;/ul&gt;&lt;li&gt;Lack of Adobe support. Adobe handed AIR over to a subsidiary of &lt;a href=&quot;https://www.harman.com/&quot;&gt;Samsung (Harman)&lt;/a&gt; in &lt;a href=&quot;https://blog.adobe.com/en/publish/2019/05/30/the-future-of-adobe-air&quot;&gt;June 2019&lt;/a&gt;for ongoing maintenance. Support is still provided by Harman, but only for enterprises with legacy applications that they still rely on, There&#39;s no active innovation or features being added to AIR by Harman.&lt;li&gt;Security concerns. As with Flash in the browser, security was always an ongoing issue, and this continued in AIR since it was the backbone to its core functionality. By continuing to build on AIR, it poses security risks and compatibility limitations with modern browsers and operating systems.&lt;li&gt;Lack of developer interest and ecosystem. Developers on the modern web tend to favour open ecosystems with an active community for support and updates. Adobe AIR’s ecosystem has completely stagnated.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-9&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;As with any other Flash-based technology, I&#39;m afraid not, it is no longer supported and even if you could, there are more modern open frameworks you could use like, &lt;a href=&quot;https://reactnative.dev/&quot;&gt;React Native&lt;/a&gt;, &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt;, or &lt;a href=&quot;https://www.electronjs.org/&quot;&gt;Electron&lt;/a&gt; (for desktop applications). AIR is now history, and if you are using an AIR application within your digital estate, it is &lt;strong&gt;strongly recommended&lt;/strong&gt; you prioritise migration, due to higher maintainability, poor security, and lack of developer availability.&lt;h3 id=&quot;yahoo-pipes&quot;&gt;Yahoo Pipes&lt;/h3&gt;&lt;p&gt;It is easy to forget just how dominant Yahoo was on the web during the late 1990s. Before Google emerged as the leading search engine, Yahoo was one of the primary gateways to the internet. Its peak influence was between 1996 and 2000, when it played a central role in how people accessed and navigated the web. It was the default starting point for most web users due to its curated directory, news, and email services combined. It was also a technology leader on the web too, as I mention later in the blog post when I look at their extensive JS Library: &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#yahoo-user-interface-yui-2006&quot;&gt;Yahoo! User Interface (YUI)&lt;/a&gt;.&lt;p&gt;I remember using Yahoo Pipes for combining my many RSS feeds at the time, it really was a fantastic visual tool for data manipulation.&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-800.webp 800w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Image of the innovative data manipulation tool with its drag and drop UI.&quot; decoding=&quot;async&quot; height=&quot;524&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/rn9Imq_Ru6-800.png 800w&quot; width=&quot;800&quot;&gt;&lt;/picture&gt;&lt;h4 id=&quot;what-was-it&quot;&gt;What was it?&lt;/h4&gt;&lt;p&gt;Yahoo pipes was a visual data mashup tool that allowed users to aggregate, manipulate, and filter data from around the web. It was released in 2007, and it allowed developers (and non-developers) alike to aggregate, manipulate, and filter data from around the web. It provided a drag and drop interface where users could connect various manipulation modules and join them together by creating pipes between modules. You were essentially piping data &quot;through&quot; the tool (hence the name!) and the manipulated date would come out the other end. It was considered highly innovative at the time, and it was used a lot for rapid prototyping.&lt;h4 id=&quot;why-is-it-outdated-4&quot;&gt;Why is it outdated?&lt;/h4&gt;&lt;p&gt;Have a look at the &lt;a href=&quot;https://www.yahoo.com/&quot;&gt;Yahoo! homepage&lt;/a&gt;, and you will see it is the shadow of its former self, it looks to more of a news aggregation service now than a popular search engine. This was because Yahoo decided to make a giant shift in business strategy, moving away from developer tools and open web utilities to concentrate on advertising and media products. Although it was popular with technology enthusiasts, it was never a mainstream product for Yahoo, so the operational expenses vs. usage statistics didn&#39;t align with Yahoo&#39;s business priorities. Lastly, the web evolved beyond Yahoo Pipes, and it couldn&#39;t keep pace with the changes. Modern APIs, JSON-based services, and JS frameworks allowed developers to build similar data transformations programmatically with greater flexibility. Due to all these factors Yahoo Pipes was sadly shut down in 2015.&lt;h4 id=&quot;can-i-still-use-it-today-2&quot;&gt;Can I still use it today?&lt;/h4&gt;&lt;p&gt;Nope, it was shutdown by Yahoo in 2015, with no further support or hosting by Yahoo.&lt;h4 id=&quot;modern-alternatives-12&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;While innovative at the time, visual mashups have been replaced by:&lt;ul&gt;&lt;li&gt;Dedicated data transformation tools (e.g. &lt;a href=&quot;https://zapier.com/&quot;&gt;Zapier&lt;/a&gt;, &lt;a href=&quot;https://www.make.com/en&quot;&gt;Integromat/Make&lt;/a&gt;).&lt;li&gt;Serverless functions (&lt;a href=&quot;https://aws.amazon.com/lambda/&quot;&gt;AWS Lambda&lt;/a&gt;, Azure Functions, &lt;a href=&quot;https://workers.cloudflare.com/&quot;&gt;Cloudflare Workers&lt;/a&gt;, and &lt;a href=&quot;https://www.fastly.com/documentation/guides/compute/getting-started-with-compute/&quot;&gt;Fastly Compute@Edge&lt;/a&gt;) for real-time data processing.&lt;li&gt;Low-code platforms with integrated API management.&lt;/ul&gt;&lt;p&gt;The web has also become a lot more complicated regarding Web scraping and feed aggregation. Because anti-scraping measures, authentication, and API rate limits weren&#39;t necessary when Yahoo Pipes was created, the techniques it employed didn&#39;t support the robust backend processes now required to handle these requirements. Although Yahoo Pipes was innovative at the time, it has long been discontinued and is now considered a obsolete part of web platform history.&lt;h3 id=&quot;phonegap-apache-cordova&quot;&gt;PhoneGap / Apache Cordova&lt;/h3&gt;&lt;p&gt;The one thing that sticks in my mind when I think about PhoneGap is when I saw a talk from one of the &lt;a href=&quot;https://www.nitobi.com/&quot;&gt;Nitobi&lt;/a&gt; engineers back in 2009 / 2010, he said something along the lines of:&lt;blockquote&gt;&lt;p&gt;We are using PhoneGap to bridge the current gap for developers in creating cross-platform mobile applications. Our goal is for it to become obsolete once native platforms fully support these capabilities.&lt;/blockquote&gt;&lt;p&gt;This really impressed me at the time, spending so much time on a product with the &lt;strong&gt;aim&lt;/strong&gt; for it to become obsolete.&lt;h4 id=&quot;what-was-phonegap&quot;&gt;What was PhoneGap?&lt;/h4&gt;&lt;p&gt;PhoneGap was a mobile development framework created by a Canadian company called &lt;a href=&quot;https://www.nitobi.com/products/phonegap/index&quot;&gt;Nitobi&lt;/a&gt; in 2009, it was later acquired by Adobe in 2011. PhoneGap allowed web developers to build cross-platform mobile applications, simply, using web technologies: HTML, CSS, and JS. The applications were packaged into native containers allowing them to run as mobile apps, while also having access to device API&#39;s via JS.&lt;h4 id=&quot;why-was-it-required&quot;&gt;Why was it required?&lt;/h4&gt;&lt;p&gt;At the time of release, the mobile web was incredibly popular and getting bigger month on month. It&#39;s important to remember, the first version of the iPhone was released only 2-years before (June 2007). This really was an exciting time in the Web Platform&#39;s history. If you wanted to release a cross-platform application at the time and wanted to support Android, iOS, and Windows Phone, you needed developers with knowledge of multiple programming languages:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Android&lt;/strong&gt; required Java.&lt;li&gt;&lt;strong&gt;iOS&lt;/strong&gt; required Objective-C.&lt;li&gt;&lt;strong&gt;Windows Phone&lt;/strong&gt; required C#.&lt;/ul&gt;&lt;p&gt;Finding a single developer with all these skills would be incredibly hard, so to build and maintain all 3 platforms usually required a whole team of developers.&lt;p&gt;One of the main advantages of PhoneGap was that all 3 platforms had a single central codebase, which reduced development time and maintenance.&lt;p&gt;PhoneGap leveraged &lt;a href=&quot;https://cordova.apache.org/&quot;&gt;Cordova&lt;/a&gt; under the hood, which it essentially branded and wrapped for broader adoption.&lt;h4 id=&quot;why-is-it-outdated-5&quot;&gt;Why is it outdated?&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;PhoneGap apps performed poorly&lt;/strong&gt;. This was especially true for graphics-intensive, or animation-heavy interfaces. This is because PhoneGap apps ran within a WebView container rather than as a native application.&lt;li&gt;&lt;strong&gt;Adobe stopped supporting it&lt;/strong&gt;. This seems to be a common theme in this blog post… Adobe ended support for PhoneGap in October 2020. At the time developers were advised to either migrate to &lt;a href=&quot;https://cordova.apache.org/&quot;&gt;Apache Cordova&lt;/a&gt; or consider other frameworks.&lt;li&gt;&lt;strong&gt;Alternatives evolved&lt;/strong&gt;. As the mobile platform expanded, so did the availability of other frameworks to help developers build apps. These alternatives included:&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://reactnative.dev/&quot;&gt;React Native&lt;/a&gt; allowing near-native performance with JS and React paradigms.&lt;li&gt;&lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; enabling high-performance apps with a single Dart codebase and native compilation.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps&quot;&gt;Progressive Web Apps (PWAs)&lt;/a&gt; reducing the need for wrapping web apps as native apps in many use cases.&lt;li&gt;&lt;a href=&quot;https://capacitorjs.com/&quot;&gt;Capacitor (by Ionic)&lt;/a&gt; providing modern native bridging with a streamlined developer experience compared to PhoneGap/Cordova.&lt;/ol&gt;&lt;li&gt;&lt;strong&gt;PhoneGap&#39;s Ecosystem growth stalled&lt;/strong&gt;. As newer frameworks were released and Adobe stopped supporting it the community moved away and PhoneGap’s plugin ecosystem stagnated.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-10&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;No, there are several alternatives I have listed above that you should consider instead. PhoneGap served its initial purpose as a bridge, enabling developers to build cross-platform mobile applications. As was its mission, It became obsolete when native platforms fully incorporated these capabilities.&lt;h3 id=&quot;microsoft-silverlight&quot;&gt;Microsoft Silverlight&lt;/h3&gt;&lt;p&gt;NOTE: I never used Silverlight (I do remember it being announced) I&#39;m just adding it to the post for completeness.&lt;h4 id=&quot;what-is-silverlight&quot;&gt;What is Silverlight?&lt;/h4&gt;&lt;p&gt;Silverlight was a rich internet application (RIA) framework introduced by Microsoft in 2007. It was conceptually similar to Adobe Flash, designed to deliver interactive multimedia, animations, and streaming video inside the browser.&lt;p&gt;It used a subset of the .NET Framework, with applications typically written in C# or VB.NET, and presentation defined using XAML (an XML-based UI markup language). Developers could reuse existing .NET skills, which made Silverlight attractive in Microsoft-centric enterprises.&lt;p&gt;Silverlight was often used for:&lt;ol&gt;&lt;li&gt;Media streaming (notably Netflix in its early streaming days)&lt;li&gt;Interactive dashboards and line-of-business web apps&lt;li&gt;Cross-browser, cross-platform plugins (Windows and Mac were supported, but Linux support lagged)&lt;/ol&gt;&lt;h4 id=&quot;why-is-it-considered-legacy-2&quot;&gt;Why is it considered legacy?&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Plugin dependency&lt;/strong&gt;: By the 2010s browser vendors had moved away from browser plugins in favour of newly developed web platform technologies. Plugins were often unsecure, unstable, and inaccessible.&lt;li&gt;&lt;strong&gt;Limited cross-platform reach&lt;/strong&gt;: Although Silverlight was well supported on Microsoft platforms (as you would expect!), it also had support on Mac, but it had limited support on Linux (via project Moonlight), and no support on mobile devices (Android, iOS).&lt;li&gt;&lt;strong&gt;Rise of open web standards&lt;/strong&gt;: HTML5, CSS3, and JavaScript rose so rapidly for audio, video and advanced graphics (via canvas). The use of plugins was no longer required.&lt;li&gt;&lt;strong&gt;End of support&lt;/strong&gt;: Considering the above points, Microsoft only stopped support in October 2021. Although Browser vendors stopped a long time before: Chrome in 2015, Edge never supported it. Firefox ended support in March 2017.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-11&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Well, this is another easy one. No, you can&#39;t Microsoft have dropped support and no Modern browsers support it either.&lt;h4 id=&quot;modern-alternatives-13&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;The answer to this is basically native web platform API&#39;s. Specifics include:&lt;ul&gt;&lt;li&gt;Video Streaming&lt;ul&gt;&lt;li&gt;&lt;strong&gt;HTML5 &lt;code&gt;&amp;lt;video&gt;&lt;/code&gt; element&lt;/strong&gt; with adaptive bitrate streaming (&lt;a href=&quot;https://en.wikipedia.org/wiki/HTTP_Live_Streaming&quot;&gt;HLS&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP&quot;&gt;MPEG-DASH&lt;/a&gt;).&lt;li&gt;&lt;strong&gt;DRM&lt;/strong&gt; is handled via &lt;a href=&quot;https://www.w3.org/TR/encrypted-media-2/&quot;&gt;Encrypted Media Extensions (EME)&lt;/a&gt;.&lt;/ul&gt;&lt;li&gt;For interactive apps and dashboards:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Modern JavaScript frameworks&lt;/strong&gt; such as React, Angular, Vue, or Svelte.&lt;li&gt;&lt;strong&gt;WebAssembly (Wasm)&lt;/strong&gt; for near-native performance, including options like &lt;a href=&quot;https://dotnet.microsoft.com/en-us/apps/aspnet/web-apps/blazor&quot;&gt;Blazor (from Microsoft)&lt;/a&gt; which lets you run .NET in the browser without plugins.&lt;/ul&gt;&lt;li&gt;For graphics, animation, and UI:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/animation&quot;&gt;&lt;strong&gt;CSS3 animations and transforms&lt;/strong&gt;&lt;/a&gt; for UI transitions.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/canvas&quot;&gt;&lt;strong&gt;Canvas API&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;&lt;strong&gt;WebGL&lt;/strong&gt;&lt;/a&gt; for 2D and 3D graphics.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/SVG&quot;&gt;&lt;strong&gt;SVG&lt;/strong&gt;&lt;/a&gt; for scalable vector graphics.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGPU_API&quot;&gt;&lt;strong&gt;WebGPU&lt;/strong&gt;&lt;/a&gt; (emerging) for modern GPU-accelerated rendering.&lt;/ul&gt;&lt;/ul&gt;&lt;h4 id=&quot;silverlight-summary&quot;&gt;Silverlight Summary&lt;/h4&gt;&lt;p&gt;Silverlight is legacy because it relied on a now-obsolete plugin model, had poor cross-platform support, and was outpaced by open web standards. Today, everything Silverlight did can be done more securely and portably with HTML5, CSS, JavaScript frameworks, and WebAssembly.&lt;h3 id=&quot;java-applets&quot;&gt;Java Applets&lt;/h3&gt;&lt;p&gt;NOTE: I never used Java Applets (although I remember them!) I&#39;m just adding it to the post for completeness.&lt;h4 id=&quot;what-is-java-applets&quot;&gt;What is Java Applets?&lt;/h4&gt;&lt;p&gt;Java Applets were small applications written in Java that could be embedded into web pages and run inside the browser through a special Java Plug-in (based on the &lt;a href=&quot;https://en.wikipedia.org/wiki/NPAPI&quot;&gt;NPAPI plugin architecture&lt;/a&gt;). Introduced in the mid-1990s, they were part of &lt;a href=&quot;https://en.wikipedia.org/wiki/Sun_Microsystems&quot;&gt;Sun Microsystems’&lt;/a&gt; vision of “write once, run anywhere” – letting developers build interactive content and complex functionality that browsers of the time (pre-HTML5) could not support natively.&lt;p&gt;They were often used for:&lt;ul&gt;&lt;li&gt;Interactive educational content and simulations&lt;li&gt;Online games&lt;li&gt;Financial tools like mortgage calculators or trading dashboards&lt;li&gt;Enterprise intranet applications&lt;/ul&gt;&lt;h4 id=&quot;why-is-it-considered-legacy-3&quot;&gt;Why is it considered legacy?&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Plugin dependency&lt;/strong&gt;: To use the Applets this required a user to install the &lt;a href=&quot;https://docs.oracle.com/goldengate/1212/gg-winux/GDRAD/java.htm#BGBFJHAB&quot;&gt;Java Runtime Environment (JRE)&lt;/a&gt; plugin and keep it updated. I distinctly remember the &lt;a href=&quot;https://p0w3rsh3ll.wordpress.com/wp-content/uploads/2013/06/jre-1-7-update-prompt.jpg&quot;&gt;update prompt&lt;/a&gt; for these nuisance updates!&lt;li&gt;&lt;strong&gt;Security risks&lt;/strong&gt;: The Java plugin was a frequent target of exploits and malware, leading browsers and enterprises to actively block or disable it.&lt;li&gt;&lt;strong&gt;Performance and user experience&lt;/strong&gt;: Applets often loaded slowly, had inconsistent UI integration with web pages, and required clunky permission dialogs.&lt;li&gt;&lt;strong&gt;Decline of NPAPI support&lt;/strong&gt;: Browsers started phasing out NPAPI (the plugin technology Applets relied on). &lt;strong&gt;Chrome&lt;/strong&gt; dropped NPAPI in 2015, &lt;strong&gt;Firefox&lt;/strong&gt; dropped NPAPI in 2017 (except Flash until 2021) , and Microsoft Edge never supported NPAPI.&lt;li&gt;&lt;strong&gt;Official deprecation&lt;/strong&gt;: Oracle deprecated the Java browser plugin in Java 9 (2017) and removed it entirely in later releases.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-12&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Nope! Modern browser no longer support it and Oracle stopped supporting the plugin in 2017.&lt;h4 id=&quot;modern-alternatives-14&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;This list is basically native web platform API&#39;s. I don&#39;t want to repeat myself so refer to the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-13&quot;&gt;Silverlight Modern Alternatives&lt;/a&gt; from earlier in the post.&lt;h4 id=&quot;java-applet-summary&quot;&gt;Java Applet Summary&lt;/h4&gt;&lt;p&gt;Java Applets are legacy because they relied on a fragile plugin model that posed significant security risks and is no longer supported by modern browsers. Today, HTML5, JavaScript, and WebAssembly provide richer, faster, and safer alternatives without requiring any plugins.&lt;h2 id=&quot;5-the-javascript-framework-explosion&quot;&gt;5. The JavaScript Framework Explosion&lt;/h2&gt;&lt;h3 id=&quot;dhtml-beginnings-1997&quot;&gt;DHTML Beginnings (1997)&lt;/h3&gt;&lt;p&gt;Dynamic HTML (DHTML) was all the rage around 1997-1998. By combining the primary web technologies: HTML, CSS, JS, and the DOM, developers realised that they could make a web page interactive and &quot;dynamically&quot; update the page without the need to reload the entire page. This new technique was frequently employed for animated HTML elements, such as image rollovers and dynamic navigation menus. It also provided immediate user feedback, particularly for form validation, by checking if the user had entered a valid email, for instance. If not available, then use standard HTML and CSS to show a user-friendly error message.&lt;p&gt;DHTML wasn&#39;t necessarily bad, if implemented in moderation, unfortunately it always seemed like the wild-west. What would work in Internet Explorer 4 (IE4) wouldn&#39;t always work in Netscape Navigator because Microsoft and Netscape Communications Corporation (NCC), had different levels of support for JavaScript in fact, Microsoft had their own version of the ECMAScript standard called JScript. This led to lots of maintenance headaches for developers as the solution often involving forking code for different browsers.&lt;p&gt;Secondly it turned into a bit of Copy / Paste madness. Since any developer could simply &quot;View Source&quot; on a web page and copy the code and add it to their own website, you often ended up with a mishmash of different interactions and animations all over a website! Thankfully, the trend eventually died out!&lt;h4 id=&quot;can-i-still-use-it-13&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;A web developer could technically still use DHTML on the modern web, but doing so would be strongly discouraged for any serious or production-level work. This is because in using it, a developer wouldn’t be following modern best practices like separating concerns with structured HTML, CSS and JS, building accessible and performant interfaces, using modular and maintainable code, or leveraging modern frameworks and tooling that enforce consistency, security and scalability.&lt;h4 id=&quot;modern-alternatives-15&quot;&gt;Modern alternatives&lt;/h4&gt;&lt;p&gt;There are several modern alternatives to DHTML, including:&lt;ul&gt;&lt;li&gt;Using Frameworks like React, Vue, or Svelte&lt;li&gt;Native browser APIs&lt;li&gt;Modern CSS techniques&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;Progressive enhancement&lt;/a&gt; and accessibility standards&lt;/ul&gt;&lt;h3 id=&quot;early-frameworks-and-libraries&quot;&gt;Early frameworks and libraries&lt;/h3&gt;&lt;h4 id=&quot;prototype-js-2005&quot;&gt;Prototype.js (2005)&lt;/h4&gt;&lt;p&gt;Prototype.js version 1.0 was released in February 2005, and was initially developed to help simplify JS tasks in Ruby on Rails (RoR) projects. Its key features were a host of DOM manipulation utilities, AJAX abstraction to make &lt;code&gt;XMLHttpRequest&lt;/code&gt; easier to handle cross-browser, Class-based inheritance in the form of a lightweight object-oriented programming (OOP) in JS. But by far its most influential feature was its shorthand DOM element selector &lt;code&gt;$()&lt;/code&gt;, later popularised by jQuery.&lt;p&gt;I remember trying to learn and use Prototype a few times, but as a JS beginner, I found the name confusing. Especially since the prototype object sits at the heart of JavaScript, with almost the whole language hanging off it through things like property and method inheritance.&lt;h4 id=&quot;can-i-still-use-it-14&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;I mean, technically you could, but be warned it hasn&#39;t been updated in &lt;a href=&quot;http://prototypejs.org/&quot;&gt;almost 10-years&lt;/a&gt;! Given the significant evolution of JS and the availability of modern, updated alternatives that leverage the latest browser JS APIs, it would not be a wise choice.&lt;h4 id=&quot;modern-alternatives-16&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;It really depends on what a developer was using prototype.js for, as it had quite a range of functionality:&lt;h5 id=&quot;dom-manipulation&quot;&gt;DOM manipulation&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://262.ecma-international.org/6.0/&quot;&gt;Native JS (ES6+)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/fabiospampinato/cash&quot;&gt;Cash&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://umbrellajs.com/&quot;&gt;Umbrella JS&lt;/a&gt;&lt;/ul&gt;&lt;h5 id=&quot;ajax&quot;&gt;AJAX&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch&quot;&gt;fetch&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://axios-http.com/docs/intro&quot;&gt;Axios&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/sindresorhus/ky&quot;&gt;Ky&lt;/a&gt;&lt;/ul&gt;&lt;h5 id=&quot;utility-functions&quot;&gt;Utility functions&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://lodash.com/&quot;&gt;Lodash&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://selfrefactor.github.io/rambda/#/&quot;&gt;Rambda&lt;/a&gt;&lt;/ul&gt;&lt;h5 id=&quot;templating&quot;&gt;Templating&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://handlebarsjs.com/&quot;&gt;Handlebars&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&quot;&gt;ES6 Template literals&lt;/a&gt;&lt;/ul&gt;&lt;h5 id=&quot;full-replacement&quot;&gt;Full replacement&lt;/h5&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://jquery.com/&quot;&gt;jQuery&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://react.dev/&quot;&gt;React&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;Prototype.js was incredibly powerful. My preference would be to utilise several micro-JS libraries for specific functionalities rather than adopting an extensive framework such as React, but that&#39;s just my opinion, given the complexity of the React ecosystem.&lt;h4 id=&quot;script-aculo-us-2005&quot;&gt;Script.aculo.us (2005)&lt;/h4&gt;&lt;p&gt;Script.aculo.us v1.0 was released in 2005 as an extension to Prototype.js. It built on Prototype.js by delivering a powerful set of visual effects, animations, and UI components. It also featured Drag-and-drop support out of the box, as well as sortable lists and autocompletion widgets. As with Prototype.js, Script.aculo.us popularity was partly because it was bundled with RoR, and it had widespread adoption within the &quot;Rails&quot; community.&lt;p&gt;I still remember Script.aculo.us for its &lt;a href=&quot;http://script.aculo.us/&quot;&gt;distinctive URL&lt;/a&gt;, its bright, animated homepage, and how it really embodied the spirit of ‘web 2.0,’ making the web feel more alive. It&#39;s a library that left a lasting legacy, influencing later libraries like jQuery UI.&lt;h4 id=&quot;can-i-still-use-it-15&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;I really wouldn&#39;t recommend it, as it hasn&#39;t been updated in over 15 years! However, if you&#39;re curious to delve into some web 2.0 history, the site is &lt;a href=&quot;http://script.aculo.us/&quot;&gt;still live&lt;/a&gt; (on HTTP, not HTTPS).&lt;h4 id=&quot;modern-alternatives-17&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;Assuming you are only looking for a JS library for animations:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://greensock.com/gsap&quot;&gt;GSAP (GreenSock Animation Platform)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://motion.dev/&quot;&gt;Motion One (Vanilla JS)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://popmotion.io&quot;&gt;Popmotion&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://animejs.com&quot;&gt;Anime.js&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API&quot;&gt;CSS + Web Animations API (WAAPI)&lt;/a&gt;&lt;/ul&gt;&lt;h4 id=&quot;dojo-toolkit-2005&quot;&gt;Dojo Toolkit (2005)&lt;/h4&gt;&lt;p&gt;Dojo Toolkit was one of the first major cross-browser toolkits, released in March 2005 with version 0.1. It was developed by &lt;a href=&quot;https://infrequently.org/&quot;&gt;Alex Russell&lt;/a&gt; and was also maintained by a community of other developers too. It&#39;s open-source and still available on &lt;a href=&quot;https://github.com/dojo/dojo&quot;&gt;GitHub&lt;/a&gt; It was one of the earliest frameworks to help build rich web applications by simplifying DOM manipulation, AJAX, event handling, animations and internationalisation (i18n), among many other cutting-edge features. Not only that, it was an early advocate for Asynchronous Module Definition (AMD) and modular JS. Furthermore, it was one of the first JS libraries with strong accessibility (a11y) support. In 2018 Dojo was rewritten as Dojo 2 which supports TypeScript, reactive patterns, virtual DOM, and modern build systems. Dojo 1.x is still in use in some long-running enterprise applications on the web. The last stable release was released in 2022, which was mainly for bug fixes and security updates. New feature development has now shifted purely to Dojo 2+.&lt;p&gt;Dojo 1.x was incredibly influential for later JS libraries like jQuery, MooTools, Prototype, especially when it came to governance. It was governed by the &lt;a href=&quot;https://download.dojotoolkit.org/release-0.4.4/dojo-0.4.4-src/documents/web0.3/foundation/&quot;&gt;Dojo Foundation&lt;/a&gt;, which later merged with the &lt;a href=&quot;https://blog.jquery.com/2012/03/06/announcing-the-jquery-foundation/&quot;&gt;jQuery Foundation&lt;/a&gt; to form the &lt;a href=&quot;https://venturebeat.com/dev/node-js-and-js-foundations-are-merging-to-form-openjs/&quot;&gt;JS Foundation&lt;/a&gt; (now part of the &lt;a href=&quot;https://openjsf.org/&quot;&gt;OpenJS Foundation&lt;/a&gt;).&lt;h4 id=&quot;can-i-still-use-it-16&quot;&gt;Can I still Use it?&lt;/h4&gt;&lt;p&gt;Version 1.x, would be a bad idea, but you could &lt;em&gt;technically&lt;/em&gt; still use the latest version (8.0.0), although that may not be wise either, given there hasn&#39;t been a new release in &lt;a href=&quot;https://github.com/dojo/framework/releases/tag/v8.0.0&quot;&gt;over 3-years&lt;/a&gt;. So it&#39;s most likely better to stick with more modern framework alternatives.&lt;h4 id=&quot;modern-alternatives-18&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;There are a number of modern alternatives you could consider, keeping in mind that Dojo was very focussed on Accessibility (A11y) and Internationalisation (i18n):&lt;ol&gt;&lt;li&gt;React: Maintained by: Meta (Facebook).&lt;ul&gt;&lt;li&gt;A11y: Strong support, but it’s &lt;strong&gt;developer-driven&lt;/strong&gt;. ARIA roles and keyboard navigation must be implemented explicitly by developers.&lt;li&gt;i18n: Excellent ecosystem (&lt;code&gt;react-intl&lt;/code&gt;, &lt;code&gt;formatjs&lt;/code&gt;, &lt;code&gt;lingui&lt;/code&gt;, etc.)&lt;/ul&gt;&lt;li&gt;Vue.js (v3): Maintained by: &lt;a href=&quot;https://evanyou.me/&quot;&gt;Evan You&lt;/a&gt; and the Vue core team.&lt;ul&gt;&lt;li&gt;A11y: Good defaults; still &lt;strong&gt;developer-led&lt;/strong&gt;, but accessible components are emerging.&lt;li&gt;i18n: &lt;code&gt;vue-i18n&lt;/code&gt; is well-maintained and powerful.&lt;/ul&gt;&lt;li&gt;Angular: Maintained by: Google&lt;ul&gt;&lt;li&gt;A11y: Arguably the best among mainstream frameworks. The Angular Material team publishes a11y guidance, and many baked-in best practices exist.&lt;li&gt;i18n: Built-in i18n support, including message extraction and compile-time translation.&lt;/ul&gt;&lt;li&gt;Svelte / SvelteKit: Maintained by: &lt;a href=&quot;https://bsky.app/profile/rich-harris.dev&quot;&gt;Rich Harris&lt;/a&gt; and the Svelte core team&lt;ul&gt;&lt;li&gt;A11y: Improving, but not as mature as React or Angular. Accessible components need to be &lt;strong&gt;explicitly chosen or built&lt;/strong&gt;.&lt;li&gt;i18n: Community libraries exist (&lt;code&gt;svelte-i18n&lt;/code&gt;), but official support is not as comprehensive.&lt;/ul&gt;&lt;/ol&gt;&lt;h4 id=&quot;yahoo-user-interface-yui-2006&quot;&gt;Yahoo! User Interface (YUI) (2006)&lt;/h4&gt;&lt;p&gt;YUI was released in February 2006, its first release was version 2.0.0, this is because there was lots of internal development and usage within Yahoo! before it was released publicly. It was originally designed to standardise frontend development at Yahoo and provide the team with a solid cross-browser solution on which the Yahoo team could build feature-rich web applications. It contained a custom loader system that only loaded the components it needed, this was an ingenious approach in the pre-ECMAScript 6 (ES6) module era. Furthermore, it also came with a whole host of feature rich UI widgets, cross-browser abstraction, Event handling, DOM utilities, Animations, CSS Tools (which heavily influencing &lt;a href=&quot;https://nicolasgallagher.com/about-normalize-css/&quot;&gt;Normalize.css&lt;/a&gt; that was developed later), and it was one of the first libraries (after Dojo 1.x) to prioritise internationalisation (i18n) and accessibility, specifically &lt;a href=&quot;https://www.w3.org/WAI/standards-guidelines/aria/&quot;&gt;Accessible Rich Internet Applications (ARIA)&lt;/a&gt;.&lt;p&gt;What I remember most about YUI 2.x is just how huge it was! Not just the sheer number of UI modules, but the file size too: 300–350 KB minified, or 90–120 KB gzipped! This was before the widespread availability of fast broadband, and when hardware and browsers were significantly less optimised. A full build of the library could easily exceed those figures, too. This is why Yahoo also provided a combination aware CDN service to help reduce the number of requests made and bundle &lt;strong&gt;only&lt;/strong&gt; the components needed. This was a practice that was way ahead of its time!&lt;h4 id=&quot;can-i-still-use-it-17&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;No, not really, it hasn&#39;t been updated since 2014, the reason for this is detailed in this &lt;a href=&quot;https://yahooeng.tumblr.com/post/96098168666/important-announcement-regarding-yui&quot;&gt;Yahoo Engineering announcement&lt;/a&gt; from the time.&lt;h4 id=&quot;modern-alternatives-19&quot;&gt;Modern alternatives&lt;/h4&gt;&lt;p&gt;Given YUI&#39;s extensive nature as a framework, and to avoid repetition, I recommend referencing my notes on &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-14&quot;&gt;Prototype.js modern alternatives&lt;/a&gt; as an initial guide.&lt;h4 id=&quot;moo-fx-2005&quot;&gt;moo.fx (2005)&lt;/h4&gt;&lt;p&gt;Moo.fx was a lightweight animation library designed to be &lt;a href=&quot;https://en.wikipedia.org/wiki/Unobtrusive_JavaScript&quot;&gt;unobtrusive&lt;/a&gt; and it also worked well with Prototype.js. It focused purely on DOM animations like height transitions and fading etc. It was part of a movement in JS to modularise the codebase for lighter and more responsive interactions. Furthermore, it was a distinct diversion away from larger and heavier animation libraries like &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#script-aculo-us-2005&quot;&gt;Script.aculo.us&lt;/a&gt;.&lt;p&gt;I believe moo.fx was one of the first animation libraries I ever saw. Annoyingly the site hasn&#39;t been archived on &lt;a href=&quot;https://web.archive.org&quot;&gt;archive.org&lt;/a&gt;. I do remember it having a simple and colourful homepage with examples of the animations you could add by only adding a tiny library to your page (3 KB).&lt;p&gt;It also had a fantastic URL too &quot;http://moo.fx&quot;. What&#39;s so cool about the name, you may ask? Well, the &lt;code&gt;.fx&lt;/code&gt; country code top-level domain (ccTLD) has now been rendered obsolete and is no longer available. It was originally reserved for Metropolitan France, but it was never officially delegated or made available for registration. France later adopted &lt;code&gt;.fr&lt;/code&gt; as its official ccTLD. I can&#39;t fathom how Valerio Proietti, the creator of moo.fx, managed to register the name, but it&#39;s all true. As proof only a single record can be found on archive.org, dating back to 2007, it links to the site&#39;s &lt;a href=&quot;https://web.archive.org/web/20070925000000*/http://moo.fx/robots.txt&quot;&gt;robots.txt file&lt;/a&gt;.&lt;h4 id=&quot;can-i-still-use-it-18&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;I can&#39;t even find an archived copy of the homepage, let alone the library itself! So, it definitely falls into the &quot;no, you can&#39;t still use it&quot; category!&lt;h4 id=&quot;modern-alternatives-20&quot;&gt;Modern Alternatives&lt;/h4&gt;&lt;p&gt;Assuming we are only looking for a modern animation library, it&#39;s best to refer to the list I gave above for &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-15&quot;&gt;Script.aculo.us modern alternatives&lt;/a&gt;.&lt;h4 id=&quot;mootools-2006&quot;&gt;MooTools (2006)&lt;/h4&gt;&lt;p&gt;The author of &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#moo-fx-2005&quot;&gt;moo.fx&lt;/a&gt;, Valerio Proietti wanted more than an animation library. He aimed to develop a complete JS framework with an Object-Oriented Programming, modularity and extensibility as first-class features. Thus, MooTools was born! v1.0 was released in September 2006, and it featured some fantastic features for its lightweight size. These included a modular core, and separate components, you could also include, for extra functionality, an Advanced class system (predating the ES6 &lt;code&gt;class&lt;/code&gt; functionality). Powerful DOM manipulation utilities, Ajax handling, effects (moo.fx), and custom events. It was both performant (for the time) and syntactically elegant. Development ceased in the mid-2010&#39;s with v1.5 (the final active release). moo.fx and MooTools hold a special place in my memory as one of the first JS libraries I learnt as a Junior developer.&lt;h4 id=&quot;can-i-still-use-it-19&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Well, the website still exists &lt;a href=&quot;https://mootools.net/&quot;&gt;here&lt;/a&gt;, But considering it hasn&#39;t been updated since January 2016, it&#39;s probably best to look for a &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-14&quot;&gt;modern alternative&lt;/a&gt;.&lt;h4 id=&quot;jquery-core-2006&quot;&gt;jQuery (core) (2006)&lt;/h4&gt;&lt;p&gt;In 2006, a truly revolutionary JS library was released called &lt;a href=&quot;https://jquery.com/&quot;&gt;jQuery&lt;/a&gt;. It was developed by the legendary &lt;a href=&quot;https://johnresig.com/&quot;&gt;John Resig&lt;/a&gt;. It offered a lightweight, chainable API in JS to simplify tasks like DOM traversal, and Ajax, among many other &lt;a href=&quot;https://api.jquery.com/&quot;&gt;helpful tools and methods&lt;/a&gt; in its fantastically written API.&lt;p&gt;jQuery always prided itself on its easy-to-use API and its ability to abstract away the many cross-browser bugs related to different browser vendors implementation of JS (Mozilla) / JScript (Microsoft). It finally gave developers a &quot;stable&quot; API to start building JS powered websites without all the stress of the cross-browser hacks, and forking of code to make features work in every browser. With jQuery, it just worked!&lt;p&gt;I must admit, I absolutely adored jQuery (and still do)! The API was so clean and readable. The complete opposite to the DOM and the JS API! This library has saved more than just my code, it’s rescued entire projects and probably saved my sanity in the process! Especially working in Digital Marketing, as I did at the time. In those days, clients were constantly after the newest, flashiest animations, regardless of usability. It was all about chasing trends. And when the client&#39;s paying, you just nod and make that already bouncing button pulse and change colour!&lt;h5 id=&quot;can-i-still-use-it-20&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Finally, I can say &quot;Yes&quot; to this question! Assuming you plan to use &lt;a href=&quot;https://jquery.com/download/&quot;&gt;v3.x of jQuery&lt;/a&gt;, as the 1.x and 2.x branches are no longer supported or maintained. In fact version 4.0 is currently in beta, according to the &lt;a href=&quot;https://jquery.com/support/&quot;&gt;Support page&lt;/a&gt;. Amazingly, almost a full 19-years after its first release, it is still under active maintenance! Not only that, according to the &lt;a href=&quot;https://almanac.httparchive.org/en/2024/&quot;&gt;Web Almanac 2024&lt;/a&gt;, it&#39;s still the &lt;a href=&quot;https://almanac.httparchive.org/en/2024/javascript#library-usage&quot;&gt;most popular JavaScript library in use on the web&lt;/a&gt;! Truly impressive work by the jQuery team and community!&lt;h3 id=&quot;later-era-frameworks&quot;&gt;Later era frameworks&lt;/h3&gt;&lt;h4 id=&quot;ext-js-2007&quot;&gt;Ext.js (2007)&lt;/h4&gt;&lt;p&gt;Ext.js version 1.0 was released in April 2007. It was initially developed as an &lt;strong&gt;extension to the YUI Library&lt;/strong&gt; before becoming a fully standalone framework. Its key features included a comprehensive suite of rich UI components, a powerful event model, and advanced layout management capabilities that far exceeded most contemporaries. It introduced a highly structured approach to building web applications, with a strong emphasis on reusable widgets and object-oriented design. But by far its most distinctive contribution was its fully integrated, desktop-like component model for the web. Something rarely seen at the time, and which set the tone for many enterprise-grade JS frameworks that followed.&lt;p&gt;I never used YUI personally, as its sheer size and breadth of functionality simply didn’t align with the kind of work I was doing at the time. As a result, Ext.js (which as mentioned above, was initially built upon YUI) wasn’t on my radar either. That being said, I’ve included it here for completeness, as it clearly played a significant role in the evolution of rich client-side application frameworks. During my research I discovered how Ext.js transformed into an enterprise-grade toolkit under the &lt;a href=&quot;https://www.sencha.com/products/extjs/&quot;&gt;Sencha brand&lt;/a&gt;. Its strong emphasis on data-driven UIs distinguished it from other lightweight libraries of that period.&lt;h5 id=&quot;can-i-still-use-it-21&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Yes, Ext.js is still a viable option that you can use on the modern web, if you are building a web application. It continues to be actively maintained by &lt;a href=&quot;https://store.sencha.com/&quot;&gt;Sencha&lt;/a&gt; and even offers a &lt;a href=&quot;https://www.sencha.com/products/reext/&quot;&gt;React Extension&lt;/a&gt;, allowing for seamless integration of Ext.js components into React applications. However, be aware that Ext.js is now a &lt;a href=&quot;https://store.sencha.com/&quot;&gt;paid library&lt;/a&gt;, with a per-year, per-developer licensing model that can be costly. While a &lt;a href=&quot;https://store.sencha.com/&quot;&gt;free community version exists&lt;/a&gt;, it appears to have a very limited feature set.&lt;h4 id=&quot;jquery-ui-2007&quot;&gt;jQuery UI (2007)&lt;/h4&gt;&lt;p&gt;jQuery UI emerged in 2007 as an official companion library to jQuery, at a time when the JS ecosystem was fragmented and riddled with browser inconsistencies. It was developed to bring a unified, extensible suite of widgets, effects, and interactions to the web. jQuery UI offered an easy-to-integrate API that drastically lowered the barrier for implementing rich UI&#39;s, with full cross-browser compatibility. It played a crucial role in making dynamic front-end behaviour accessible to developers at all skill levels, becoming a staple in both enterprise and amateur hobbyist applications during the formative years of modern web development.&lt;p&gt;Although I was a big fan of jQuery and used it extensively across many projects, I never really had the opportunity to use jQuery UI in its entirety. When I did, it was typically for a single component, such as a date picker or drag-and-drop functionality. These components were reliable and well-supported, but required a lot of JS to function, and added complexity that I never felt was acceptable for a single feature. Especially when there were plenty of alternative micro-frameworks available, offering small, focused libraries that solved one problem well. I was far more inclined to take that modular approach than to include an entire suite of UI components unnecessarily.&lt;p&gt;One resource, I found invaluable at the time, was &lt;a href=&quot;https://microjs.com/&quot;&gt;MicroJS&lt;/a&gt;. It hasn’t been updated in quite some time, but it remains a powerful illustration of how easy it is to cherry-pick the exact only the functionality you need, without burdening your page with hundreds of kilobytes of JS.&lt;h5 id=&quot;can-i-still-use-it-22&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;For this question, just as I did with &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#jquery-core-2006&quot;&gt;jQuery&lt;/a&gt;, my answer is yes, you can still use it. It isn&#39;t updated very often, but it is still updated! The last release being in &lt;a href=&quot;https://github.com/jquery/jquery-ui/releases/tag/1.14.1&quot;&gt;October 2024 with version 1.14.1&lt;/a&gt;&lt;p&gt;To put the enduring popularity of jQuery and jQuery UI into perspective, the &lt;a href=&quot;https://almanac.httparchive.org/en/2024/javascript#fig-38&quot;&gt;2024 Web Almanac&lt;/a&gt; reports that jQuery is still the most-used JS library on the web, appearing on 74% of pages in the &lt;a href=&quot;https://almanac.httparchive.org/en/2024/methodology&quot;&gt;dataset analysed&lt;/a&gt;. jQuery UI comes in fourth, with a 22% usage rate, Though described as mostly deprecated, it’s a clear reminder of how quickly modern tools become legacy software that must be maintained for decades. The latest release came nearly 18 years after jQuery UI’s first version. That’s an incredible achievement by the jQuery UI team, talk about dedication!&lt;h4 id=&quot;angularjs-2010&quot;&gt;AngularJS (2010)&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;AngularJS&lt;/strong&gt; and &lt;strong&gt;Angular&lt;/strong&gt; are fundamentally different frameworks that share a name and lineage but are otherwise entirely different. AngularJS (1.x) was based on a Model-View-Controller (MVC) architecture with two-way data binding written in JS with support for ECMAScript 5 (ES5) and some ECMAScript (ES6) features. Angular (2+) was built entirely differently, it has a component-based architecture boasts stronger modularity, enabling both two-way binding and promoting unidirectional data flow. Another major difference is that Angular is written in TypeScript, a distinct superset of JS, that enables better tooling and type safety like Java and many other languages. &lt;strong&gt;Angular&lt;/strong&gt; remains actively used and maintained on the modern web today.&lt;p&gt;I distinctly remember when Google released AngularJS because I was in Melbourne, Australia, working at a digital media agency. One of the Tech Directors there was raving about it, and how it was going to change Frontend development entirely. In hindsight, I agree with him, but I, personally, don’t believe it was a positive change. Single Page Apps (SPA’s) have had such a huge negative impact on Web Performance and Accessibility. Plus, a lot of the code in many of these SPA frameworks essentially reinvents functionality that’s already built into the browser and the Web Platform as a whole. Let&#39;s not overcomplicate things with framework-managed page state; we have a perfectly good back and forward buttons, thank you very much!&lt;p&gt;As you can probably tell, I’m not a big fan of SPA&#39;s. Admittedly, they have their place under some circumstances; However, I genuinely think they&#39;re excessive and unnecessarily complicate frontend web development for most applications. But I guess complex is the new simple, right? Anyway, rant over!&lt;h5 id=&quot;can-i-still-use-it-23&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;&lt;a href=&quot;https://angularjs.org/&quot;&gt;AngularJS&lt;/a&gt;, no. It is no longer actively maintained. &lt;a href=&quot;https://angular.dev/&quot;&gt;Angular&lt;/a&gt;, absolutely, it is still a very popular framework on the modern web, although according to the &lt;a href=&quot;https://2024.stateofjs.com/&quot;&gt;State of JS 2024 survey&lt;/a&gt;, it has been overtaken by &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue.js&lt;/a&gt; for usage, for the &lt;a href=&quot;https://2024.stateofjs.com/en-US/libraries/front-end-frameworks/&quot;&gt;2nd year running&lt;/a&gt;. Frontend developers can be fickle, often chasing the next shiny framework like a kitten distracted by a dangling set of keys. It will be interesting to see if this decline in Angular usage continues in future JS surveys.&lt;h4 id=&quot;backbone-2010&quot;&gt;Backbone (2010)&lt;/h4&gt;&lt;p&gt;&lt;a href=&quot;https://backbonejs.org/&quot;&gt;Backbone.js&lt;/a&gt; was released in 2010 by Jeremy Ashkenas, who also created &lt;a href=&quot;https://underscorejs.org/&quot;&gt;Underscore.js&lt;/a&gt; and &lt;a href=&quot;https://coffeescript.org/&quot;&gt;CoffeeScript&lt;/a&gt;. As with AngularJS it was one of the first JS libraries that implemented the MVC architecture pattern to client-side JS. The key features of Backbone were its models, collections, views, router, events, and sync functionality that allowed it to easily communicate with RESTful API&#39;s.&lt;p&gt;I&#39;ve only ever worked on a Backbone project once, and it was a rapid prototype website for a major airline based in Asia. Given the tight timelines and the client’s high design expectations, we ultimately opted for static HTML in the end, as Backbone’s complexity wasn’t advantageous for a rapid prototyping. In hindsight, had it got to the production stage, then I could see the architecture of backbone being very useful.&lt;h5 id=&quot;can-i-still-use-it-24&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Backbone is nowhere near as popular as modern frameworks like React, Vue, or Angular, It is still mostly in use on legacy systems. The last version released was in April 1st 2025 v1.6.1, but looking through &lt;a href=&quot;https://github.com/jashkenas/backbone/tags&quot;&gt;the releases&lt;/a&gt;, it seems to only get 1 update per year. According to Wappalyzer it is still in use by around &lt;a href=&quot;https://www.wappalyzer.com/technologies/javascript-frameworks/backbone-js/&quot;&gt;521,000 websites&lt;/a&gt;. The biggest of those being &lt;a href=&quot;https://www.atlassian.com/&quot;&gt;Atlassian&lt;/a&gt;. So in my opinion I&#39;d say you should avoid using it and opt for a more popular framework with a more active community. Refer to the modern alternatives I listed for &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-16&quot;&gt;Dojo Toolkit Modern Alternatives&lt;/a&gt; as a starting point.&lt;h4 id=&quot;knockout-2010&quot;&gt;Knockout (2010)&lt;/h4&gt;&lt;p&gt;During the early 2010s, &lt;a href=&quot;https://knockoutjs.com/&quot;&gt;Knockout.js&lt;/a&gt; was a popular JS library for building dynamic UIs using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel&quot;&gt;Model-View-ViewModel (MVVM)&lt;/a&gt; pattern. It offered features like declarative bindings and two-way data synchronisation, which made it easier to keep the UI in sync with underlying data without manually manipulating the DOM. Its simplicity, ease of learning, and lack of required tooling, e.g. just drop in a &lt;code&gt;&amp;lt;script&gt;&lt;/code&gt; tag into an HTML page and go, this made it especially appealing at a time when frontend complexity was just beginning to accelerate. Though now largely superseded by modern frameworks, Knockout played a key role in the evolution of reactive web development.&lt;p&gt;I only used Knockout once for a client in Australia, while I lived there. It was a project that had been passed between offices because it had become a maintenance nightmare. Had it been a better planned build, it would have been a more pleasant experience, but time constraints meant a rapid delivery was prioritised over build quality.&lt;h5 id=&quot;can-i-still-use-it-25&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;The last release was way back in &lt;a href=&quot;https://github.com/knockout/knockout/releases/tag/v3.5.1&quot;&gt;November 2019&lt;/a&gt;, so there&#39;s no way it should be used on a modern website build. Although, according to &lt;a href=&quot;https://www.wappalyzer.com/&quot;&gt;Wappalyzer&lt;/a&gt; it is still used by almost &lt;a href=&quot;https://www.wappalyzer.com/technologies/javascript-frameworks/knockout-js/&quot;&gt;43,000 websites&lt;/a&gt;. So you are very unlikely to come across it, even in legacy systems.&lt;h5 id=&quot;modern-alternatives-21&quot;&gt;Modern Alternatives&lt;/h5&gt;&lt;p&gt;If you are looking a modern MVVM framework then &lt;a href=&quot;https://vuejs.org/&quot;&gt;Vue.js&lt;/a&gt;, &lt;a href=&quot;https://svelte.dev/&quot;&gt;Svelte&lt;/a&gt;, or &lt;a href=&quot;https://aurelia.io/&quot;&gt;Aurelia&lt;/a&gt; are the way to go as the all fully support the MVVM architectural pattern. But if you aren&#39;t bothered about MVVM then refer to the list of modern alternatives I listed for &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#modern-alternatives-16&quot;&gt;Dojo Toolkit&lt;/a&gt; earlier in the post.&lt;h2 id=&quot;6-css-workarounds-and-browser-quirks&quot;&gt;6. CSS Workarounds and Browser Quirks&lt;/h2&gt;&lt;h3 id=&quot;old-css-practices&quot;&gt;Old CSS practices&lt;/h3&gt;&lt;h4 id=&quot;sliding-doors-technique&quot;&gt;Sliding Doors Technique&lt;/h4&gt;&lt;p&gt;In the time before &lt;code&gt;border-radius:&lt;/code&gt; existed in CSS, this was a legitimate way of “faking” the look of rounded borders. The technique&#39;s name reflects its implementation and purpose. (nothing to do with the &lt;a href=&quot;https://www.imdb.com/title/tt0120148/&quot;&gt;1998 film starring Gwyneth Paltrow&lt;/a&gt; about getting cheated on!). It was actually a rather ingenious method of creating rounded corners that expanded and contracted both in width and height of the bounding box containing the content. It was also incredibly useful for making your site navigation more interesting. If you are interested in reading all about this technique, check out &quot;&lt;a href=&quot;https://alistapart.com/article/slidingdoors/&quot;&gt;Sliding Doors of CSS&lt;/a&gt;&quot; and &quot;&lt;a href=&quot;https://alistapart.com/article/slidingdoors2/&quot;&gt;Sliding Doors of CSS, Part II&lt;/a&gt;&quot; on &lt;a href=&quot;https://alistapart.com/&quot;&gt;alistapart.com&lt;/a&gt; both from October 2003!&lt;p&gt;If you are old like me, you can just jump back into full nostalgia mode by visiting the &lt;a href=&quot;https://alistapart.github.io/code-samples/slidingdoors/v1/ex5.html&quot;&gt;final code example here&lt;/a&gt;!&lt;h5 id=&quot;can-i-still-use-it-26&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;There&#39;s absolutely no need to use it on the modern web, simply use the &lt;code&gt;border-radius&lt;/code&gt; CSS property, it comes with all the positives of the sliding door&#39;s technique (rounded corners), and none of the negatives (higher maintenance, additional downloads, non-semantic wrapper DIVS used only for presentation). And given its &lt;a href=&quot;https://caniuse.com/border-radius&quot;&gt;browser support on the modern web&lt;/a&gt;, you can go rounded corner crazy!&lt;h4 id=&quot;image-sprites-for-icons&quot;&gt;Image Sprites for Icons&lt;/h4&gt;&lt;p&gt;The history of icon sprites is actually quite a fascinating one (for me at least!), since it is essentially rooted in web performance optimisation. The HTTP/1.1 specification (&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616&quot;&gt;RFC 2616&lt;/a&gt;) published in June 1999, specifically says in &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616#section-8.1.4&quot;&gt;section 8.1.4&lt;/a&gt; that:&lt;blockquote&gt;&lt;p&gt;Clients that use persistent connections SHOULD limit the number of simultaneous connections that they maintain to a given server. A single-user client SHOULD NOT maintain more than 2 connections with any server or proxy.&lt;/blockquote&gt;&lt;p&gt;HTTP/1.1 introduced persistent connections, a departure from the one-off connections of HTTP/1.0 (as detailed in &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc2616#section-8.1&quot;&gt;section 8.1 of the HTTP/1.1 specification&lt;/a&gt;). This innovation understandably raised concerns within the Network Working Group regarding potential increases in server load across the web.&lt;p&gt;The web was becoming more and more popular every day around that time, with thousands of new websites being launched daily. Remember the Dot-com &lt;a href=&quot;https://www.tickerhistory.com/p/the-dot-com-bubble-explained&quot;&gt;Bubble that burst in March 2000&lt;/a&gt;, it gives you some indication of just how popular the internet had become across the world!&lt;p&gt;You&#39;re probably thinking &quot;what has any of this got to do with image sprites?&quot;, well, quite a lot, actually. With HTTP/1.1, every asset on a page meant opening a new TCP connection. So if your site had hundreds of icons, the browser would literally open hundreds of separate connections just to fetch them all (assuming it were allowed to)! And this is why browser connection limits were introduced into browsers. Different browsers chose different limits, but it was usually around 4 to 6 &lt;strong&gt;per domain&lt;/strong&gt;. So in order to reduce the number of TCP connections to download all these image assets, a technique called Image Sprites was invented.&lt;p&gt;Image Sprites, involved compiling all your small image assets down into a single bigger &lt;strong&gt;background&lt;/strong&gt; image file, then using CSS positioning (and width / height) to essentially mask the image and only show the single image you wanted to show in any particular place. It then only required a single TCP connection to download all the small images (one large image with all the icons), and you&#39;d use some CSS magic to just reposition the background image. This technique was mostly used for image icons, and the CSS for this method would look something like this:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.icon&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;sprite.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -20px -40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 16px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we have an icon that is 16px by 16px and the icon we want to show is positioned at -20px -40px on the background image. Genius! A working example of image sprites can be found &lt;a href=&quot;https://codepen.io/chriscoyier/pen/rZPPOd&quot;&gt;here on CodePen&lt;/a&gt;.&lt;h5 id=&quot;can-i-still-use-it-27&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Again, no need. Image sprites fell out of favour as a technique when more modern techniques like &lt;a href=&quot;https://ryantrimble.com/blog/what-the-heck-is-an-svg-sprite-sheet.html&quot;&gt;SVG sprites&lt;/a&gt; and &lt;a href=&quot;https://fontawesome.com/&quot;&gt;webfont icons&lt;/a&gt; came along.&lt;p&gt;The technique was also outdated by new web performance technologies like:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2019/12/17/http2-and-sri-dont-always-get-on/#2-http-2-connection-coalescing&quot;&gt;HTTP/2 + Connection Coalescing&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2023/09/13/enabling-http3-on-govuk/&quot;&gt;HTTP/3 + QUIC&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;But those are topics for another blog post, which I just so happen to have written a little about, so check them out (shameless self-promotion, sorry!)&lt;h4 id=&quot;vendor-prefixes-for-css&quot;&gt;Vendor Prefixes for CSS&lt;/h4&gt;&lt;p&gt;Now I&#39;m pretty sure some readers will be thinking, wait &quot;vendor prefixes for CSS are old best practice??&quot;. They really aren&#39;t as they are a vital technique to stop new CSS additions from &quot;breaking the web&quot;. By slapping on a vendor prefix to a CSS property allows browser vendors to experiment with new CSS features in isolation and then once stabilised in the CSS specifications, and supported by the major browsers, the prefix can be removed in favour of the non-prefixed version. For example, &lt;code&gt;-moz-border-radius&lt;/code&gt; changes to &lt;code&gt;border-radius&lt;/code&gt;. So just to clarify vendor prefixes &lt;strong&gt;aren&#39;t&lt;/strong&gt; old best practice, but the &lt;em&gt;way&lt;/em&gt; they are created within website code &lt;strong&gt;is&lt;/strong&gt;. In the distant past you used to have to manually write out each of the prefixed versions to ensure maximum compatibility with all browsers. Can you imagine writing this out manually for all new CSS features you want to use! Example:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.mybox&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;-webkit-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Chrome, Safari, iOS Safari (early versions) */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;-moz-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    &lt;span class=&quot;token comment&quot;&gt;/* Firefox (pre-version 4) */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;-ms-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;/* Not officially supported, but some old syntax examples include it */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;-o-border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;/* Old Opera (Presto engine, now obsolete) */&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;/* Standard syntax */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;What a waste of bytes and time! (I know compression pretty much fixes the bytes issue!) With the popularity of tools like &lt;a href=&quot;https://web.archive.org/web/20190815095909/http://compass-style.org/&quot;&gt;Compass&lt;/a&gt; (a Sass Framework), &lt;a href=&quot;https://lesscss.org/&quot;&gt;LESS&lt;/a&gt; mixins, and &lt;a href=&quot;https://www.bourbon.io/&quot;&gt;Bourbon&lt;/a&gt; (a Sass mixin library), or even &lt;a href=&quot;https://stylus-lang.com/&quot;&gt;Stylus&lt;/a&gt; with the &lt;code&gt;nib&lt;/code&gt; plugin, the issue started to be abstracted away from developers.&lt;h5 id=&quot;can-i-still-use-them-2&quot;&gt;Can I still use them?&lt;/h5&gt;&lt;p&gt;On the modern web there&#39;s simply no need to &lt;strong&gt;manually&lt;/strong&gt; add prefixes. The best practice is to use &lt;a href=&quot;https://postcss.org/&quot;&gt;PostCSS&lt;/a&gt; with &lt;a href=&quot;https://github.com/postcss/autoprefixer&quot;&gt;Autoprefixer&lt;/a&gt;. Or if you’re using bundlers like &lt;a href=&quot;https://webpack.js.org/&quot;&gt;webpack&lt;/a&gt;, &lt;a href=&quot;https://vite.dev/&quot;&gt;Vite&lt;/a&gt;, or &lt;a href=&quot;https://parceljs.org/&quot;&gt;Parcel&lt;/a&gt;, they all offer out-of-the-box support for Autoprefixer. &lt;a href=&quot;https://rollupjs.org/&quot;&gt;Rollup&lt;/a&gt; also supports Autoprefixer but requires a plugin. With the use of these tools along with &lt;a href=&quot;https://browsersl.ist/&quot;&gt;Browserslist&lt;/a&gt; frontend developers don&#39;t even have to think about prefixes any more (once properly configured). An automation win for all developers!&lt;h4 id=&quot;heavy-use-of-important-in-css&quot;&gt;Heavy Use of !important in CSS&lt;/h4&gt;&lt;p&gt;In the early days of CSS the use of &lt;code&gt;!important&lt;/code&gt; was a common practice for the enforcement of style overrides. It was often considered a &quot;quick fix&quot; for specificity issues, and used to fix visual inconsistencies in cross-browser scenarios or when using third-party CSS code. However, the overuse of &lt;code&gt;!important&lt;/code&gt; is now widely considered a legacy practice that causes major issues with scalability, testability, and maintenance.&lt;h5 id=&quot;can-i-still-use-it-28&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Yes, It can still be used in &lt;strong&gt;certain circumstances&lt;/strong&gt; e.g. quick debugging, third-party overrides. But a rule of thumb I&#39;d recommend is, if you are using &lt;code&gt;!important&lt;/code&gt; for &lt;strong&gt;anything&lt;/strong&gt; outside a small set of circumstances, you likely have bigger problems that need to be addressed first. As having predictable specificity in CSS is critical for the long term &quot;health&quot; of any web UI. Its use is a clear red flag that the project may be difficult to work on, as it’s a distinct frontend code smell.&lt;p&gt;That being said, If you ever &lt;strong&gt;do&lt;/strong&gt; need to use it, you should clearly document the reason(s) why in the code, so future developers clearly understand why. Without clear documentation on why it is being used, a future developer is likely to remove this legacy CSS practice due to its reputation, potentially disrupting page styling elsewhere on the website.&lt;h3 id=&quot;oldie-hacks&quot;&gt;OldIE hacks&lt;/h3&gt;&lt;p&gt;For anyone lucky enough to have missed the joy of building websites for IE 5, 5.5, and 6 (the browser trilogy nobody asked for, released between 1999 and 2001), rest assured, that statement is absolutely, definitely not sarcastic at all. Promise! These versions of IE were just terrible browsers to work with. Microsoft in their infinite wisdom were following their own interpretation of the browser standards that were still in their infancy, and this often creating proprietary features that no other browser vendor followed or implemented.&lt;p&gt;Bundled with Windows XP, the popular IE6 unfortunately held back web progress for several years. Its standalone release for Windows 98, Windows ME, Windows NT 4.0, and Windows 2000 further cemented its widespread adoption and prolonged influence across the web. This forced Microsoft to innovate in the browser market, or face obsolescence, when a viable and revolutionary competitor finally emerged to finally move the web forwards! Initially launched in 2002 as &lt;a href=&quot;https://blog.mozilla.org/community/2013/05/13/milestone-phoenix-0-1-released-first-version-of-firefox/&quot;&gt;Phoenix 0.1&lt;/a&gt;, a name symbolising its rise from the ashes of &lt;a href=&quot;https://web.archive.org/web/20080228001822/http://moz.sillydog.org/archives/netscape_9006.php&quot;&gt;Netscape Navigator 9.0.0.6&lt;/a&gt;, this browser was renamed Firebird in early 2003, and later that same year became Firefox, the name it retains today.&lt;p&gt;And as an avid Firefox user for many years, I&#39;m still delighted it is still around as it has its own HTML engine called Gecko. As the modern browser market is very &lt;a href=&quot;https://webkit.org/&quot;&gt;WebKit&lt;/a&gt; / &lt;a href=&quot;https://www.chromium.org/blink/&quot;&gt;Blink&lt;/a&gt; dominated. Competition in this area is always a good thing to help drive innovation, and move the web forwards!&lt;p&gt;So, enough of the dusty old history books, what was so bad about IE6? Oh, dear, where do I even begin? It was less &quot;web browser&quot; and more &quot;digital dumpster fire&quot; with a whole host of issues and annoyances listed below. As for the &quot;Can I still use it?&quot; section, trust me you wouldn&#39;t want too! oldIE is thankfully about as common on the modern web as seeing a third-party script that improves web performance! Let us have a look at many oldIE issues in all their glory:&lt;h4 id=&quot;doctype-fragility&quot;&gt;DOCTYPE fragility&lt;/h4&gt;&lt;p&gt;If there was a single character before the DOCTYPE in IE6 it would trigger quirks mode (see the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#quirks-mode-layouts&quot;&gt;Quirks Mode Layouts&lt;/a&gt; section earlier in the post for a more detailed explanation). This even included newline, comment, space, or invisible byte characters. As you can imagine this was a nightmare for debugging. And if you weren&#39;t in full control of your HTML coming from the server, you were in for a bad time! Quirks mode essentially set the browser into the legacy rendering mode (mostly IE5 behaviour). If that wasn&#39;t bad enough, it would also interpret the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Box_model&quot;&gt;Box model&lt;/a&gt; differently, causing chaos for layouts! Lastly CSS and layout behaviour would be inconsistent or in some cases, totally broken!&lt;h4 id=&quot;zoom-1-hack&quot;&gt;zoom: 1 hack&lt;/h4&gt;&lt;p&gt;The &lt;code&gt;zoom: 1&lt;/code&gt; hack forced an element into the &quot;hasLayout&quot; rendering mode, which was a proprietary internal rendering concept in IE6 and IE7. For any element that didn&#39;t “have layout”, it could:&lt;ul&gt;&lt;li&gt;Collapse when floated&lt;li&gt;Break rendering of child elements&lt;li&gt;Overflow improperly&lt;li&gt;Fail to clear floats&lt;li&gt;Interrupt margin collapse&lt;/ul&gt;&lt;p&gt;I distinctly remember using &lt;code&gt;zoom: 1&lt;/code&gt; a lot with float layouts, which was the main option for CSS layouts at the time. As mentioned above the element would then clear floating elements and also overflow elements correctly.&lt;h4 id=&quot;underscore-hack&quot;&gt;Underscore Hack&lt;/h4&gt;&lt;p&gt;This was a pretty simple hack to target only &lt;strong&gt;IE6&lt;/strong&gt; and &lt;strong&gt;IE7&lt;/strong&gt; in &lt;strong&gt;quirks mode&lt;/strong&gt;, any property prefixed by an underscore is ignored by modern browsers, but recognised by IE6 and IE7.&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.selector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;_width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 500px &lt;span class=&quot;token comment&quot;&gt;/* targets IE6 / IE7qm */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 500px &lt;span class=&quot;token comment&quot;&gt;/* modern browsers */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;asterisk-hack&quot;&gt;Asterisk Hack&lt;/h4&gt;&lt;p&gt;Another simple hack to target IE6 and IE7 that is ignored by modern browsers. It was especially useful for fixing box model issues.&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.selector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	*&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px &lt;span class=&quot;token comment&quot;&gt;/* targets IE6 &amp; IE7 */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100px &lt;span class=&quot;token comment&quot;&gt;/* modern browsers */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;star-html-hack&quot;&gt;Star HTML Hack&lt;/h4&gt;&lt;p&gt;A slightly different hack that was added to the start of the selector rather than a property within a selector. It would only target IE6 in &lt;strong&gt;standards mode&lt;/strong&gt; (not quirks mode).&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html .selector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* all browsers including IE6 */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 50px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;* html .selector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* IE6 only - order is important */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The order of the above code is important since IE6 could read both selectors but the second one &quot;wins&quot; due to its position in the cascade.&lt;h4 id=&quot;child-selector-hack&quot;&gt;Child Selector hack&lt;/h4&gt;&lt;p&gt;Another straightforward hack to target ONLY modern browsers (not IE6) was:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;ul &gt; li&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* not understood by IE6 &amp; IE7 */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; rebeccapurple&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* &quot;rebeccapurple&quot; not understood by IE6 &amp; IE7 */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;IE6 &amp; IE7 didn&#39;t understand the child selector, so it simply ignored it.&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: the colour is also historically significant in the web community as the &lt;code&gt;rebeccapurple&lt;/code&gt; CSS color keyword was added as a tribute to web pioneer &lt;a href=&quot;https://meyerweb.com/&quot;&gt;Eric Meyer&lt;/a&gt;&#39;s daughter &lt;a href=&quot;https://meyerweb.com/eric/thoughts/category/personal/rebecca/&quot;&gt;Rebecca&lt;/a&gt; who passed away of brain cancer at the age of six. Eric, if I’m ever lucky enough for you to see this post, please know that as a brain cancer survivor myself who has personally seen the impact this terrible disease has on friends and family, my heart truly goes out to you and your family. I am so sorry for what you’ve all been through. Rest in peace, Rebecca.&lt;h4 id=&quot;double-margin-float-bug&quot;&gt;Double Margin Float Bug&lt;/h4&gt;&lt;p&gt;This is a pretty aptly named CSS bug fix, as it does exactly what it says on the tin! Due to IE6&#39;s crazy box model interpretation it sometimes liked to double the margin on floated elements.&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.floated-element&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* fix the double margin bug in IE6 */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;peekaboo-bug-fix&quot;&gt;Peekaboo bug fix&lt;/h4&gt;&lt;p&gt;IE6 sometimes threw its toys out of the pram when content changed on the page, in these cases it would either render them incorrectly or just make them disappear, hence the name peekaboo!&lt;p&gt;To fix it, you had to apply &quot;hasLayout&quot; to the element:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.buggy-element&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;zoom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* trigger hasLayout on the element to fix the peekaboo bug */&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;transparent-png-fix&quot;&gt;Transparent PNG fix&lt;/h4&gt;&lt;p&gt;IE6 was unable to display the alpha channel (transparency) in PNG files, instead it just interpreted it as a grey, solid background. Combined with rounded corners on a non-solid background colour background, this was a real pain in the backside! Thankfully, IE6 supported CSS filters, allowing Microsoft to offer its proprietary AlphaImageLoader for PNG transparency. There were 2 methods to apply this filter:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.transparent-png&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;behavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;iepngfix.htc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;.htc&lt;/code&gt; file contained JS logic that dynamically applied Microsoft’s proprietary filter to the PNG elements.&lt;p&gt;An alternative for background images was this:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.transparent-bg&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;progid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;DXImageTransform.Microsoft.&lt;span class=&quot;token function&quot;&gt;AlphaImageLoader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	src=&lt;span class=&quot;token string&quot;&gt;&#39;image.png&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	sizingMethod=&lt;span class=&quot;token string&quot;&gt;&#39;crop&#39;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yep, how horrible is that! 🤮&lt;p&gt;But even if you solved the transparency issue your problems weren&#39;t over as its usage came with a few caveats:&lt;ol&gt;&lt;li&gt;AlphaImageLoader broke CSS &lt;code&gt;background-position&lt;/code&gt; and &lt;code&gt;background-repeat&lt;/code&gt;.&lt;li&gt;The fix didn&#39;t work on &lt;code&gt;&amp;lt;img&gt;&lt;/code&gt; tags unless additional &lt;code&gt;.htc&lt;/code&gt; behaviour hacks were applied!&lt;li&gt;&lt;code&gt;.htc&lt;/code&gt; files could be blocked fairly easily by companies with restrictive internet policies, so it wasn&#39;t always guaranteed to work. And if this happened to be one of your clients... well, there goes the whole design!&lt;li&gt;Lastly these hacks only worked in IE5.5 and IE6, thankfully IE7 supported transparent PNG&#39;s so these hacks had to be targeted at IE5.5 and IE6 only.&lt;/ol&gt;&lt;h4 id=&quot;lack-of-ie-developer-tools&quot;&gt;Lack of IE Developer Tools&lt;/h4&gt;&lt;p&gt;I know the bugs above sound frustrating, but one of the biggest issues was the fact that IE6 and IE7 completely lacked any &quot;sane&quot; developer tools to help resolve these issues. Initially, developers had to rely only on &lt;code&gt;alert()&lt;/code&gt; or &lt;code&gt;document.write()&lt;/code&gt; for debugging! This was where the infamous &lt;code&gt;[object Object]&lt;/code&gt; or &lt;code&gt;[object] [object]&lt;/code&gt; came from when debugging JS in IE6 and IE7. If you had a JS error that you wanted to investigate, that was just about all the information you were given from &lt;code&gt;alert()&lt;/code&gt;. Thankfully, &lt;a href=&quot;https://github.com/firebug/firebug-lite&quot;&gt;Firebug Lite&lt;/a&gt; was released later by the Firebug Working Group. It wasn&#39;t developed by Mozilla and it wasn&#39;t a browser extension. It was a JS file that you could include in your page or as a bookmarklet to mimic the Firebug debugging tools. It was only later in IE8 that Microsoft included the first native developer tool in IE. You enabled it by pressing F12, it finally allowed developers to use the &lt;code&gt;console&lt;/code&gt; functionality that we still use today. Interestingly, F12 to open the browser DevTools still remains the standard across most modern browsers!&lt;h4 id=&quot;ie-conditional-comments&quot;&gt;IE Conditional Comments&lt;/h4&gt;&lt;p&gt;Conditional comments were introduced into Internet Explorer in IE5, released in March 1999. They were introduced to Internet Explorer by Microsoft because IE had its own interpretation of HTML, CSS, and JS that diverged from W3C standards. This made it extremely difficult for Frontend developers to build consistent UI&#39;s that worked across all browsers, without resorting to complex and brittle CSS hacks as seen earlier in the post. Conditional comments allowed developers to have the best of both worlds, a fully W3C-compliant CSS file for modern browsers, and a CSS file that only applied to certain versions of Internet Explorer (that contained all the version-specific hacks!).&lt;p&gt;It&#39;s worth noting that each version of IE had its own specific hacks, so you&#39;d often see multiple Conditional Comments in the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; of a website &lt;strong&gt;after&lt;/strong&gt; the W3C-compliant stylesheet (due to CSS loaded after the modern version taking precedence due to the cascade). The hacks were still there, but they were sectioned off into their own CSS file(s), ready to be removed when the browser was no longer used by the vast majority of site users. There were countless different ways to write conditional comments due to the version logic built into the interpreter. Note that modern browsers ignored them entirely as they were seen as standard HTML comments e.g. (&lt;code&gt;&amp;lt;!--&lt;/code&gt; and &lt;code&gt;--&gt;&lt;/code&gt;).&lt;p&gt;Some example conditional comments can be found below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Target only Internet Explorer 6 --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if IE 6]&gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;styles-ie6.css&quot;&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Target IE 7 and lower --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if lte IE 7]&gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;styles-ie7-and-below.css&quot;&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Target IE 8 only --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if IE 8]&gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;styles-ie8.css&quot;&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Target IE 9 and above --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if gte IE 9]&gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;styles-ie9-and-up.css&quot;&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Target any version of IE --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if IE]&gt;
  &amp;lt;script src=&quot;polyfills-for-ie.js&quot;&gt;&amp;lt;/script&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Exclude all versions of IE (i.e. target modern browsers only) --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if !IE]&gt; --&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;modern-browser-script.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- &amp;lt;![endif]--&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Combine conditions: Target IE 6 to 8 --&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!--[if (gte IE 6)&amp;(lte IE 8)]&gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;legacy-ie6-to-ie8.css&quot;&gt;
&amp;lt;![endif]--&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Explanation of the Condition Syntax:&lt;ul&gt;&lt;li&gt;&lt;code&gt;IE&lt;/code&gt; matches any version of Internet Explorer.&lt;li&gt;&lt;code&gt;IE 6&lt;/code&gt;, &lt;code&gt;IE 7&lt;/code&gt;, &lt;code&gt;IE 8&lt;/code&gt;, etc. match specific versions.&lt;li&gt;lte = less than or equal to&lt;li&gt;gte = greater than or equal to&lt;li&gt;!IE = not Internet Explorer&lt;ul&gt;&lt;li&gt;Note the use of the &lt;code&gt;&amp;lt;!--&gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;![endif]--&gt;&lt;/code&gt; to properly close in non-IE browsers. This was known as a downlevel-revealed comment.&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;As you can see from the complexity of the above examples it was fairly simple to target very specific and multiple versions of Internet Explorer. A significant drawback was the increased maintenance burden and the clutter they introduced within the sites&#39; &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; tag.&lt;p&gt;Thankfully, Microsoft removed the parsing of conditional comments in IE10 and IE11 before they eventually introduced a whole new browser called Microsoft Edge. Edge initially used a proprietary rendering engine called &lt;a href=&quot;https://en.wikipedia.org/wiki/EdgeHTML&quot;&gt;EdgeHTML&lt;/a&gt;. However, the browser was subsequently rewritten to incorporate the same open-source engine as Google Chrome. This new version, based on Chromium 79, was released as &lt;a href=&quot;https://blogs.windows.com/msedgedev/2020/01/15/upgrading-new-microsoft-edge-79-chromium/&quot;&gt;Microsoft Edge 79&lt;/a&gt; on January 15, 2020.&lt;h4 id=&quot;ie-css-selector-limit&quot;&gt;IE CSS Selector Limit&lt;/h4&gt;&lt;p&gt;This issue, of all those detailed in this section, is arguably one of the most random, as well as one of the least noticeable! IE6 to IE9 had a selector limit of 4,095 selectors per stylesheet, now that may sound like a lot, but it was very straightforward to go over this limit, especially when grouping selectors. For Example:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* This counts as a single selector */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.my-selector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&#39;s all straightforward, but then you look at something like this:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* This block counts as three selectors */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.button-primary, .button-secondary, .button-tertiary&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 20px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once you started grouping selectors for easier maintenance it became far too easy to hit the limit, especially on large websites. If you were a user of &lt;a href=&quot;https://getbootstrap.com/&quot;&gt;Bootstrap&lt;/a&gt; or &lt;a href=&quot;https://get.foundation/&quot;&gt;Foundation&lt;/a&gt; at the time you could hit this limit unintentionally, without even knowing it!&lt;p&gt;And that brings me onto my next point: What happened when that limit was reached? Well... nothing really, IE just didn&#39;t parse &lt;strong&gt;any&lt;/strong&gt; CSS beyond the 4,095 selector limit. Would it warn you that this was happening? Absolutely not!&lt;p&gt;Developers were often extremely fortunate if this issue was discovered through testing pages styled later in the stylesheet. Internet Explorer itself, however, would simply fail without any error messages or warnings.&lt;p&gt;And, to make it even more confusing: it would only impact the specific stylesheet that had exceeded the limit, not the page as a whole. For example:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;base.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;       &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 2000 selectors --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;theme.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 4500 selectors --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;overrides.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- 300 selectors --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In IE6 - IE9 this is what would happen:&lt;ol&gt;&lt;li&gt;&lt;code&gt;base.css&lt;/code&gt; loads perfectly fine, it is under the limit.&lt;li&gt;&lt;code&gt;theme.css&lt;/code&gt; only the first 4095 selectors are parsed, the rest are silently ignored.&lt;li&gt;&lt;code&gt;overrides.css&lt;/code&gt;would be load fully since it is under the limit.&lt;/ol&gt;&lt;p&gt;This behaviour creates a partial styling issue. Elements relying on the &lt;code&gt;theme.css&lt;/code&gt; file won&#39;t be styled correctly beyond the 4095 selector limit. In such cases, most pages will appear normal until a page attempts to utilise style selectors 4096 through 4500 from the theme file, at which point it will fail without warning. And of course if you were unlucky enough to be working with IE6 or IE7, then you had no developer tools to even debug the issue either!&lt;h5 id=&quot;solution&quot;&gt;Solution&lt;/h5&gt;&lt;p&gt;So what was the solution? Well, with the invention preprocessors like Sass or tooling like Grunt, Gulp, or PostCSS they could automate the splitting of stylesheets at the 4065 limit.&lt;p&gt;Or another solution was to supply a simplified UI to IE browsers &lt;strong&gt;only&lt;/strong&gt; and serve those CSS files via IE&#39;s &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#ie-conditional-comments&quot;&gt;Conditional Comments&lt;/a&gt;. But can you imagine the maintenance involved in updating multiple different stylesheets? Just for the slightest UI change!&lt;p&gt;The final approach involved reducing reliance on external stylesheets by inlining critical CSS directly into the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; of the page, specifically for &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#above-the-fold-obsession&quot;&gt;above-the-fold&lt;/a&gt; content (we’ll come back to that strategy later, as it’s not as relevant today). Even thinking about these different maintenance options, and their implications gives me a headache!&lt;h4 id=&quot;oldie-hacks-summary&quot;&gt;OldIE hacks Summary&lt;/h4&gt;&lt;p&gt;As you can imagine CSS files around this time were a &lt;del&gt;bit&lt;/del&gt; lot of a mess with all these random cross browser hacks and workarounds! Thankfully Microsoft recognised it was an issue so decided to implement conditional comments in IE5-IE9 to make this madness a little easier (in terms of organisation, not coding)&lt;h2 id=&quot;7-markup-of-the-past&quot;&gt;7. Markup of the Past&lt;/h2&gt;&lt;h3 id=&quot;xhtml-1-1-and-2-0&quot;&gt;XHTML 1.1 and 2.0&lt;/h3&gt;&lt;p&gt;I remember having a conversation with a friend about how he was converting his website into a new standard that had just come out, this was around 2001 and the new standard was XHTML 1.1. The most obvious difference at the time was the DOCTYPE at the top of the page source. From HTML 4.01 Strict:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;HTML&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD HTML 4.01//EN&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/html4/strict.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;to:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD XHTML 1.1//EN&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;what-did-xhtml-1-1-aim-to-achieve&quot;&gt;What did XHTML 1.1 aim to achieve?&lt;/h4&gt;&lt;p&gt;The goal of this new standard was to modularise XHTML and enforce stricter XML compliance. Its key features were the fact that it was based on XHTML 1.0 Strict but split into modules for better reusability and extensibility. It also required documents to be well-formed XML, and it enforced stricter syntax than HTML like all tags must close, and all attributes must be quoted.&lt;p&gt;Unfortunately, for XHTML 1.1 it came with a number of limitations that doomed the specification from the start. These included, very little browser support for serving XHTML 1.1 as &lt;code&gt;application/xhtml+xml&lt;/code&gt;, it also, more critically broke backwards compatibility in many real-world use cases. And lastly many developers continued to write XHTML but serve it as &lt;code&gt;text/html&lt;/code&gt; which entirely defeated the point of writing XHTML in the first place!&lt;p&gt;Because it never gained wide browser support and required a very strict syntax, it eventually became obsolete and is now mostly of interest for historical or academic reasons.&lt;h4 id=&quot;what-did-xhtml-2-0-aim-to-achieve&quot;&gt;What did XHTML 2.0 aim to achieve?&lt;/h4&gt;&lt;p&gt;XHTML 2.0 was never officially released or used in any production browsers. Had it done so, this would have been the DOCTYPE:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;PUBLIC&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-//W3C//DTD XHTML 2.0//EN&quot;&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;http://www.w3.org/MarkUp/DTD/xhtml2.dtd&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Development began on the specification in the early 2000s with the key goals its key goals being:&lt;ul&gt;&lt;li&gt;a clean break from HTML 4 and XHTML 1.x.&lt;li&gt;planned to introduce entirely new ideas like replacing &lt;code&gt;&amp;lt;a&gt;&lt;/code&gt; with &lt;code&gt;&amp;lt;h&gt;&lt;/code&gt; for links.&lt;li&gt;high up on its priority list was for it to be device-agnostic and semantically pure.&lt;/ul&gt;&lt;p&gt;The XHTML 2.0 specification ultimately failed for the following reasons:&lt;ul&gt;&lt;li&gt;It lacked practical browser support and Implementation.&lt;li&gt;HTML5 (developed by &lt;a href=&quot;https://whatwg.org/&quot;&gt;WHATWG&lt;/a&gt;) gained real traction by improving the existing HTML and maintaining compatibility.&lt;li&gt;And the gigantic final nail in the XHTML 2.0 specification was its complete lack of backwards compatibility. It broke the entire ecosystem of existing web pages and tools.&lt;li&gt;The &lt;a href=&quot;https://www.w3.org/News/Public/pnews-20090706?utm_source=chatgpt.com&quot;&gt;W3C officially halted XHTML 2.0 in July 2009&lt;/a&gt; and shifted efforts to HTML5.&lt;/ul&gt;&lt;p&gt;This then brings us onto the modern standard still in use today, and finally a DOCTYPE that was easy to remember! Look how developer-friendly it is:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is so simple in fact it is case-insensitive and doesn&#39;t require a cumbersome URI to the Document Type Definition (DTD), which was there to define the structure, rules, and legal elements and attributes used on a page.&lt;h3 id=&quot;inline-javascript&quot;&gt;Inline JavaScript&lt;/h3&gt;&lt;p&gt;There are 2 distinct methods of using inline JS in an HTML document. They both have specifications in the &lt;a href=&quot;https://html.spec.whatwg.org/&quot;&gt;HTML Standard (WHATWG)&lt;/a&gt;.&lt;h4 id=&quot;inline-script-block&quot;&gt;Inline Script Block&lt;/h4&gt;&lt;p&gt;The inline block is defined in &lt;a href=&quot;https://html.spec.whatwg.org/#the-script-element&quot;&gt;section 4.12.1&lt;/a&gt; The syntax is simple and familiar. An example usage is as follows:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no-js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;utf-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Title here&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
			document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;no-js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- The script can also be placed in the body too --&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&#39;s perfectly fine to use the inline script block in this way, and it in no way a legacy / outdated technique. But it does come with a few things worth considering before using it. Inline script blocks like this cannot be run &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;defer&lt;/code&gt;, these attributes only apply when loading external scripts using the &lt;code&gt;src&lt;/code&gt; attribute.&lt;h4 id=&quot;can-i-still-use-it-29&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Yes, you can, but it comes with a few caveats. A script in this position in the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; executes immediately and &lt;strong&gt;synchronously&lt;/strong&gt;, and it will &lt;strong&gt;block page rendering&lt;/strong&gt; until the JS code completes. So make sure you don&#39;t overload an inline script block as your website will pay the price in terms of frontend web performance.&lt;h4 id=&quot;inline-event-handler-or-html-event-attribute&quot;&gt;Inline Event Handler or HTML Event Attribute&lt;/h4&gt;&lt;p&gt;Inline event handlers are defined in &lt;a href=&quot;https://html.spec.whatwg.org/#global-attributes&quot;&gt;section 3.2.6&lt;/a&gt; and &lt;a href=&quot;https://html.spec.whatwg.org/#scripting&quot;&gt;section 8.1&lt;/a&gt; of the &lt;a href=&quot;https://html.spec.whatwg.org/&quot;&gt;HTML Standard (WHATWG)&lt;/a&gt; Inline event handlers are now considered a legacy pattern in modern web development. This is due to several reasons, including the security risk posed by JS execution in the global scope, the potential for Cross-Site Scripting (XSS) vulnerabilities, and the way they clutter the code. An example of an inline event handler is below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- other page code here --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;onclick&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token language-javascript javascript value&quot;&gt;&lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;About clicked&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;About Us&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- other page code here --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The simple example above captures the &lt;code&gt;onclick&lt;/code&gt; event from the anchor, and instead of taking you to the about page which you would expect it simply brings up an alert box with the &quot;About clicked&quot; string.&lt;p&gt;There are other reasons why this technique is considered legacy too since they conflict with the principle of &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns#HTML,_CSS,_JavaScript&quot;&gt;separation of concerns&lt;/a&gt;, and also make debugging, testing, and the implementation of accessibility best practices harder. Lastly, Content Security Policy (CSP) can also disallow inline event handlers unless explicitly allowed (another big security risk!).&lt;h4 id=&quot;can-i-still-use-it-30&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;No! Don&#39;t use this outdated technique to add JS interactivity to your page. Instead you should move towards external scripts and &lt;a href=&quot;https://en.wikipedia.org/wiki/Unobtrusive_JavaScript&quot;&gt;unobtrusive JavaScript&lt;/a&gt;. An example of which is given below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- other page code here --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;myclass&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/about&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;About Us&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- other page code here --&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// assuming the element.myclass is already in the DOM&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; el &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.myclass&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Do JS stuff here!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This assumes that the &lt;code&gt;myclass&lt;/code&gt; element is already in the DOM. If it isn&#39;t &lt;code&gt;document.querySelector&lt;/code&gt; will return null and result in a TypeError. The safest way to get around this would be to use the &lt;code&gt;DOMContentLoaded&lt;/code&gt; event. An example of this given below:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DOMContentLoaded&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; el &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.myclass&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;el&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    el&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// Do JS stuff here!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If you&#39;re thinking that&#39;s a lot of code just to add a click event to a single element! Then you&#39;d be correct, hence why libraries like jQuery were so incredibly popular for &lt;a href=&quot;https://api.jquery.com/category/events/&quot;&gt;event additions&lt;/a&gt; and basic &lt;a href=&quot;https://api.jquery.com/category/manipulation/&quot;&gt;DOM manipulation&lt;/a&gt;.&lt;h3 id=&quot;document-write&quot;&gt;Document.write()&lt;/h3&gt;&lt;p&gt;I must admit, I don&#39;t think I&#39;ve every actually used &lt;code&gt;Document.write()&lt;/code&gt; on a webpage, that&#39;s probably because I&#39;ve never seen a sane reason to use it! It&#39;s a JS method provided by the browser’s DOM, that allows you to write HTML or text directly to the page. A simple example is given below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
	document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1&gt;Hello, world!&amp;lt;/h1&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wherever this code is placed in the page, it will simply output a h1 with the content of Hello, world! Now there&#39;s a reason I was so harsh on the method above, and that&#39;s because it comes with some horrible side effects. These include the following:&lt;ul&gt;&lt;li&gt;As with any Inline Script Block, it blocks page rendering until the content is written to the page.&lt;li&gt;It runs synchronously and can block scripts and other resources from loading efficiently.&lt;li&gt;Lastly, and this has to be the best (and most horrifying feature!). If used after the page has fully loaded, it can erase the entire DOM and replace it with whatever was passed to the method. This would be &lt;code&gt;&amp;lt;h1&gt;Hello, world!&amp;lt;/h1&gt;&lt;/code&gt; in the example given above.&lt;li&gt;It&#39;s important to consider the security implications of its usage as it is similar to &lt;code&gt;eval()&lt;/code&gt;(&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/(https:/developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eva)&quot;&gt;MDN link&lt;/a&gt; in some ways (but not all). Both methods can enable cross-site scripting (XSS) if user input is injected without sanitisation.&lt;/ul&gt;&lt;h4 id=&quot;when-should-it-be-used&quot;&gt;When should it be used?&lt;/h4&gt;&lt;p&gt;On the modern web the simple answer is &lt;strong&gt;never!&lt;/strong&gt; It&#39;s best seen as a historical curiosity that some legacy systems may still use that haven&#39;t been modernised yet. It could also be used in elementary educational examples or demos. There are a number of much safer (and robust) modern alternatives that should be used instead. These include:&lt;ul&gt;&lt;li&gt;&lt;code&gt;element.innerHTML&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML&quot;&gt;MDN Link&lt;/a&gt;).&lt;li&gt;&lt;code&gt;element.textContent&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent&quot;&gt;MDN Link&lt;/a&gt;).&lt;li&gt;&lt;code&gt;document.createElement()&lt;/code&gt; (&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement&quot;&gt;MDN link&lt;/a&gt;) in conjunction with &lt;code&gt;appendChild&lt;/code&gt; and &lt;code&gt;insertBefore&lt;/code&gt;.&lt;li&gt;Modern frameworks or libraries for manipulating the DOM and updating the UI.&lt;/ul&gt;&lt;h3 id=&quot;fixed-viewport-meta-tags&quot;&gt;Fixed Viewport Meta Tags&lt;/h3&gt;&lt;p&gt;Fixed viewport meta tags were used in early mobile responsive development. An example of what it looks like is below:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;My Fixed width Mobile Site&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=320, user-scalable=no&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the example above the &lt;code&gt;viewport&lt;/code&gt; meta tag is telling the browser that:&lt;ol&gt;&lt;li&gt;This website should be rendered at a fixed width of 320px&lt;li&gt;It should disable user scaling, so the layout is locked to a specific dimension, regardless of the actual screen size.&lt;/ol&gt;&lt;h4 id=&quot;why-use-this-approach&quot;&gt;Why use this approach?&lt;/h4&gt;&lt;p&gt;In the early days of mobile development, desktop websites were very difficult to view and interact with on small mobile screens. In order to &quot;fix&quot; this issue, developers often built mobile specific websites, that would sit alongside the desktop website. 320px was a popular width at the time because the first iPhone (iPhone 3G) had a 320px size screen. In order to maintain maximum control over the layout appearance on these mobile devices, developers frequently prevented users from zooming into these sites. These restrictions also helped avoid layout shifts when loading in dynamic viewport sizes e.g. device orientation (portrait vs landscape), zoom or pitch gestures, changes in browser UI elements (address bar or toolbars).&lt;h4 id=&quot;why-was-this-bad&quot;&gt;Why was this bad?&lt;/h4&gt;&lt;p&gt;There were a number of reasons as to why this technique was bad. The number one being it was terrible for accessibility. A user with a visual impairment, on a mobile site that disabled pinch-to-zoom (&lt;code&gt;user-scalable=no&lt;/code&gt;), had no way to read the site&#39;s content. Secondly, by dictating a screen width, you are harming both adaptability and making assumptions about a user&#39;s device. Devices come in all shapes, sizes, and pixel densities. Mobile, tablet, desktop, and every resolution in-between. There are literally an infinite number of screen dimensions possible, obviously many of those would be impractical for users beyond a certain range, but it&#39;s impossible to maintain all these fixed versions so this technique quickly become outdated. Lastly, fixed sizes can lead to performance issues, as they may cause unnecessary UI reflows and repaints when used with older layout methods such as tables or fixed-position elements.&lt;h4 id=&quot;modern-best-practice&quot;&gt;Modern Best Practice&lt;/h4&gt;&lt;p&gt;With the development of responsive design practices by &lt;a href=&quot;https://follow.ethanmarcotte.com/@beep&quot;&gt;Ethan Marcotte&lt;/a&gt; in 2010 (&lt;a href=&quot;https://alistapart.com/article/responsive-web-design/&quot;&gt;Responsive Web Design: A List Apart&lt;/a&gt;), fixed layouts quickly fell out of fashion, as with responsive design, you could develop a single UI that worked on all devices, no matter what their screen size or pixel density. One UI to rule them all! It comes with the huge advantages of much less maintenance for developers, and more importantly a huge usability for all users, no matter what device they are viewing a website on. The recommended &lt;code&gt;viewport&lt;/code&gt; meta tag for modern websites is this:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This tells a user&#39;s browser:&lt;ol&gt;&lt;li&gt;to match the screen’s actual width (&lt;code&gt;width=device-width&lt;/code&gt;).&lt;li&gt;to set the base zoom level (&lt;code&gt;initial-scale=1&lt;/code&gt;).&lt;li&gt;to allow users to zoom the viewport (&lt;code&gt;user-scalable=yes&lt;/code&gt; this is default if not set)&lt;/ol&gt;&lt;p&gt;You may come across fixed layouts in legacy applications, and if you do, you should seriously consider:&lt;ul&gt;&lt;li&gt;Replacing fixed tags with scalable ones.&lt;li&gt;Refactoring CSS layout logic to use &lt;a href=&quot;https://css-tricks.com/books/greatest-css-tricks/flexible-grids/&quot;&gt;flexible grids&lt;/a&gt;, &lt;a href=&quot;https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/&quot;&gt;fluid typography&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Using_media_queries&quot;&gt;media queries&lt;/a&gt;.&lt;li&gt;Ensuring accessibility standards are upheld, especially zooming support (up to 200% as specified in &lt;a href=&quot;https://www.w3.org/TR/UNDERSTANDING-WCAG20/visual-audio-contrast-scale.html&quot;&gt;WCAG Success Criterion 1.4.4&lt;/a&gt;).&lt;/ul&gt;&lt;h3 id=&quot;web-safe-fonts-only-before-font-face&quot;&gt;Web Safe Fonts Only (before @font-face)&lt;/h3&gt;&lt;p&gt;Fonts are arguably the most crucial component of the web. Without them, there would be no content, and consequently, no internet. This fundamental importance explains the extensive nature of the &lt;a href=&quot;https://drafts.csswg.org/css-fonts-4/&quot;&gt;CSS Fonts Module Level 4&lt;/a&gt; documentation. Fonts present a challenge due to their vast variety and subjective nature. What one person finds legible, another may not.&lt;p&gt;Web-safe fonts are typefaces that are broadly supported and consistently rendered across most web browsers and operating systems, eliminating the need for users to install additional fonts. They are distinguished by three primary characteristics:&lt;ol&gt;&lt;li&gt;They come pre-installed on most devices across all operating systems (Windows, macOS, Linux, iOS, and Android).&lt;li&gt;They render consistently across browsers, devices, and operating systems.&lt;li&gt;If a font isn&#39;t available on a certain device, there&#39;s a viable alternative that can be used by default, this is called &quot;fallback safety&quot;.&lt;/ol&gt;&lt;p&gt;A common fallback font family using the &lt;code&gt;font-family&lt;/code&gt; CSS property is displayed below:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.class&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Arial&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Helvetica Neue&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;According to &lt;a href=&quot;https://www.cssfontstack.com/Arial&quot;&gt;CSS Font Stack&lt;/a&gt; this combination of fonts is supported by 99.84% of devices on Windows, and 98.74% on Mac. Notice how it gives the browser a list of fonts, the primary choice being Arial, and if Arial isn&#39;t available then Helvetica Neue will be used, all the way down to &lt;code&gt;sans-serif&lt;/code&gt;. This is basically saying &quot;if none of the preceding fonts are available, then choose any &lt;a href=&quot;https://en.wikipedia.org/wiki/Sans-serif&quot;&gt;sans-serif&lt;/a&gt; font on the device&quot;. This guarantees that a font will always be available on any device, so even though different fonts will be used depending on the operating system, the page content will still be rendered and readable for all users.&lt;h4 id=&quot;common-web-safe-fonts&quot;&gt;Common Web Safe Fonts&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Arial&lt;li&gt;Times New Roman&lt;li&gt;Verdana&lt;li&gt;Georgia&lt;li&gt;Courier New&lt;li&gt;Trebuchet MS&lt;li&gt;Lucida Console&lt;/ul&gt;&lt;p&gt;The issue with these fonts is that they are very limiting, especially for the Design community. Designers have very strong opinions on fonts, it is their &quot;bread and butter&quot;, after all, so that&#39;s to be expected! For years, both developers and designers have strived to integrate all fonts, not just web-safe ones, into web development. In doing so many people on the web came up with different ways to allow them to use non-web safe fonts. These include the methods I mentioned earlier in the post:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#scalable-inman-flash-replacement-sifr&quot;&gt;Scalable Inman Flash Replacement (Sifr)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#cufon&quot;&gt;Cufón&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#gif-text-replacements&quot;&gt;GIF Text Replacements&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;As mentioned earlier all of these methods worked, but they had limitations, be that with Accessibility, Performance, Maintenance, Security, or SEO.&lt;p&gt;In order to mitigate thes limitations, a modern, standardised method was required for browsers to load custom fonts.&lt;h4 id=&quot;enter-font-face&quot;&gt;Enter @font-face&lt;/h4&gt;&lt;p&gt;&lt;code&gt;@font-face&lt;/code&gt; is a CSS rule that allows web developers to load custom fonts on a webpage. Unlike the methods listed above it&#39;s a &lt;strong&gt;native browser feature&lt;/strong&gt; that brings typographic control to the web, while also preserving Accessibility, SEO, Maintenance, Security, and Performance (if implemented correctly).&lt;p&gt;The &lt;code&gt;@font-face&lt;/code&gt; rule has a notable history, as it was initially implemented by Microsoft in&lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_Explorer_4&quot;&gt; Internet Explorer 4&lt;/a&gt; in 1997. At the time it used &lt;a href=&quot;https://en.wikipedia.org/wiki/Embedded_OpenType&quot;&gt;Embedded OpenType (EOT)&lt;/a&gt; fonts. This was a proprietary solution by Microsoft and not a part of the CSS standard at the time, so adoption outside of Microsoft browsers was non-existent. It wasn&#39;t until the W3C developed and standardised the CSS Fonts Module Level 3, that browser support across different vendors started to improve.&lt;p&gt;Although CSS Fonts Module Level 3 work began in the early 2000s, true standardisation took time as browser vendors adopted open formats like &lt;a href=&quot;https://en.wikipedia.org/wiki/TrueType&quot;&gt;TTF&lt;/a&gt;, &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenType&quot;&gt;OTF&lt;/a&gt;, and later &lt;a href=&quot;https://en.wikipedia.org/wiki/Web_Open_Font_Format&quot;&gt;WOFF and WOFF2&lt;/a&gt;. The CSS Fonts Module Level 3 was not released as a &lt;a href=&quot;https://www.w3.org/TR/css-fonts-3/&quot;&gt;W3C recommendation until September 2018&lt;/a&gt;. The first CSS Fonts Module Level 3 working draft was &lt;a href=&quot;https://www.w3.org/TR/2001/WD-css3-fonts-20010731/&quot;&gt;published in July 2001&lt;/a&gt;.&lt;h4 id=&quot;usage&quot;&gt;Usage&lt;/h4&gt;&lt;p&gt;So how do we actually use &lt;code&gt;@font-face&lt;/code&gt;? Well, it&#39;s pretty straightforward:&lt;h5 id=&quot;declaring-the-font&quot;&gt;Declaring the font&lt;/h5&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@font-face&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MyFont&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/myfont.woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	   &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&#39;/fonts/myfont.woff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;woff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	   &lt;span class=&quot;token comment&quot;&gt;/* other font formats here */&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Although you can define other font formats, this is no longer recommended, since the combination of WOFF and WOFF2 will cover all popular browsers. In fact, depending on your user analytics data, you may even be able to drop to only listing WOFF2, since it is now supported by 96.2% of browsers used on the internet according to &lt;a href=&quot;https://caniuse.com/woff2&quot;&gt;Can I Use&lt;/a&gt;.&lt;h5 id=&quot;using-the-font&quot;&gt;Using the Font&lt;/h5&gt;&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;MyFont&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here is where we apply the custom font to the page elements using standard CSS selectors.&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: note how &lt;code&gt;sans-serif&lt;/code&gt; has also been set as a fallback e.g. later in the font list. This is best practice because we are loading an external font file to render the text on the page. If this font no longer exists on the server (or simply fails to load), users will be left with no text, since the external font is missing. This ensures that even if the custom font isn&#39;t available it will &quot;fallback&quot; to an appropriate web safe font.&lt;p&gt;Now there are a number of web performance points that should be considered when using web fonts, but I won&#39;t go into them here. Instead, I will point you towards &lt;a href=&quot;https://www.zachleat.com/&quot;&gt;Zach Leat&lt;/a&gt;&#39;s excellent &quot;The Five Whys of Web Font Loading Performance&quot; article from November 2018, and it also links to his &lt;a href=&quot;https://perfnow.nl/&quot;&gt;Performance.now()&lt;/a&gt; conference talk on the same subject. Well worth a watch if you have a spare 46 minutes!&lt;h2 id=&quot;8-tools-and-workflow-relics&quot;&gt;8. Tools and Workflow Relics&lt;/h2&gt;&lt;h3 id=&quot;svn-subversion-largely-replaced-by-git&quot;&gt;SVN (subversion, largely replaced by Git)&lt;/h3&gt;&lt;p&gt;SVN (Subversion) is a centralised version control system that was widely used in the 2000s and early 2010s. It was the first versioning system I used at one of the digital agencies I worked at in the late 2000s. The memories that stick with me most about SVN are:&lt;ol&gt;&lt;li&gt;Every folder and sub-folder had an annoying &lt;code&gt;.svn&lt;/code&gt; directory within them. This directory contains all the metadata needed by SVN to manage the versioned files.&lt;li&gt;Branching and merging in SVN was a painful experience!&lt;/ol&gt;&lt;p&gt;Although in all honesty, I haven&#39;t used it in over a decade, so both these points may have changed and now be invalid? Actually, I doubt it for backwards compatibility with older SVN repositories.&lt;p&gt;The key word in the top paragraph above is &quot;centralised&quot;. In a version control context that means that with SVN there&#39;s a single central repository that all version history and file management operations are built around.&lt;p&gt;In comparison, Git / GitHub are decentralised repositories. When you clone a repository onto your local machine you have the whole history of all the files, you can modify them while offline then synchronise with other developers code modifications once you&#39;re back online.&lt;h4 id=&quot;legacy-development-practices&quot;&gt;Legacy Development Practices&lt;/h4&gt;&lt;p&gt;If SVN is being used in 2025, it could imply certain things about the codebase and teams working practices. These include:&lt;ul&gt;&lt;li&gt;The tooling being used is likely to be old (e.g. Eclipse plugins, shell scripts).&lt;li&gt;Continuous Integration (CI) / Continuous Development (CD) is likely to be very basic or missing entirely.&lt;li&gt;Due to the complexity of the branching and merging process in SVN, this type of workflow will likely be minimal if used at all!&lt;/ul&gt;&lt;h4 id=&quot;team-cultural-indicators&quot;&gt;Team Cultural Indicators&lt;/h4&gt;&lt;p&gt;There are also red flags in terms of engineering culture if SVN is still being used. It typically indicates that:&lt;ul&gt;&lt;li&gt;The engineering team has a conservative engineering culture.&lt;li&gt;The team have a risk-averse attitude to change.&lt;li&gt;The team may have a backlog of technical debt that has accumulated over many years.&lt;li&gt;Recruitment of developers wanting to use SVN is likely to be challenging, as &lt;a href=&quot;https://rhodecode.com/blog/156/version-control-systems-popularity-in-2025&quot;&gt;recent surveys&lt;/a&gt; indicate that SVN has 5.18% of the Version Control System (VCS) market share. It is second to Git that dominates with a 93.87% market share. This is also likely to impact retention of developers too, since Git / GitHub are the dominant tools in use in most industries (although, &lt;a href=&quot;https://get.assembla.com/blog/is-subversion-svn-still-used/&quot;&gt;not all&lt;/a&gt;) in 2025.&lt;/ul&gt;&lt;h4 id=&quot;what-to-look-out-for&quot;&gt;What to look out for&lt;/h4&gt;&lt;p&gt;Should you happen to encounter a project that still uses SVN for version control, you should:&lt;ul&gt;&lt;li&gt;Expect resistance to adopt modern workflows (e.g., GitFlow, CI/CD).&lt;li&gt;Investigate whether the tooling supports migration to Git e.g. &lt;code&gt;git svn&lt;/code&gt; or if a full rewrite might be needed.&lt;li&gt;Evaluate whether SVN is tightly embedded in the build and deployment process.&lt;li&gt;Prepare yourself for recruitment and retention issues as mentioned above.&lt;/ul&gt;&lt;h4 id=&quot;migration&quot;&gt;Migration&lt;/h4&gt;&lt;p&gt;Assuming you&#39;ve stumbled across a legacy project that uses SVN, and modernisation and migration is a goal for the project. It&#39;s worth knowing that:&lt;ul&gt;&lt;li&gt;SVN to Git migration tools exist (&lt;a href=&quot;https://git-scm.com/docs/git-svn&quot;&gt;git svn&lt;/a&gt; or tools like &lt;a href=&quot;https://subgit.com/&quot;&gt;SubGit&lt;/a&gt;), but edge cases can be painful.&lt;li&gt;You will likely need to retrain teams and completely refactor deployment automation.&lt;li&gt;Start with a small component or project to check see if modularisation is a feasible option.&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-31&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;TL;DR: &lt;a href=&quot;https://git-scm.com/&quot;&gt;Git&lt;/a&gt; / &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; is the way to go, for modern web development best practice.&lt;h3 id=&quot;chrome-frame&quot;&gt;Chrome Frame&lt;/h3&gt;&lt;p&gt;Chrome Frame was an ambitious project to bridge the gap between modern web standards and the many limitations of old IE versions (IE6 - IE8). It was released by Google as a plugin for IE in late 2009. It essentially embedded the Chrome browser engine into older IE versions, thus allowing the IE / Chrome hybrid to use modern web standards, modern JS frameworks, HTML5 features, all while still being compatible with an IE-centric environment. This proved particularly beneficial for larger enterprise organisations, which were reliant on older versions of IE due to legacy infrastructure and were unable to adopt more modern browsers.&lt;p&gt;While it sounded great in theory, unfortunately it came with a whole host of downsides, especially when it came to adoption. This included requiring admin access to machines in order to install the plugin, many companies locked-down the use of plugins due to the security risk involved in installation of 3rd-party code, lastly it introduced complexity for IT support teams and Quality Assurance (QA) teams due to the hybrid nature of the rendering engine.&lt;h4 id=&quot;can-i-still-use-it-32&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;No, as it was ultimately deprecated by Google in 2013, and support ended in 2014. The reasons for this were that web standards had improved and IE itself had improved with the release of IE9 and later. A significant change in browser releases was the move to &quot;evergreen browsers&quot;. These browsers update automatically in the background, without user intervention. These browser releases were untethered from specific Operating system versions, apart from Safari being a notable exception.&lt;p&gt;Although Chrome Frame only saw limited success, it certainly helped initiate discussions on migrating from legacy browsers in large enterprise environments.&lt;p&gt;I distinctly remember when it was announced I thought it would solve all our IE problems (finally!). It was only when it was released that I realised there was no way it would be able to solve the issue because:&lt;ol&gt;&lt;li&gt;It was complex to install (e.g. required admin access)&lt;li&gt;The majority of users at the time using older versions of IE were likely neither technically capable nor even interested in installing it as a plugin.&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Note&lt;/strong&gt;: This isn’t meant to sound elitist, but at the time, most people would have likely identified the internet as the &quot;blue ‘e’ icon&quot; on their desktop. Outside the web development community, few knew (or cared) what a web browser was, let alone which one they used! And I&#39;d say this statement is most likely true with the modern web too!&lt;/ol&gt;&lt;/ol&gt;&lt;h3 id=&quot;css-resets&quot;&gt;CSS Resets&lt;/h3&gt;&lt;p&gt;Before we get into the details, what is a CSS reset? It&#39;s essentially a set of CSS selectors and properties that are used to &quot;reset&quot; all styles across browsers to a common baseline. Think of it as a solid foundation on which to build your website off. In theory if all browsers render elements identically from the start, then the site will be easier to build and maintain because all those nasty minor cross-browser CSS differences will have been dealt with, that&#39;s the theory anyway.&lt;p&gt;Just to be clear, CSS resets &lt;strong&gt;are&lt;/strong&gt; still around, but they have evolved into something more forward-thinking, minimal, and only focussed on common pain points. The first CSS Reset to be released in January 2007 was &lt;a href=&quot;https://meyerweb.com/eric/tools/css/reset/&quot;&gt;Eric Meyer’s classic CSS Reset&lt;/a&gt;, it quickly became one of the most widely adopted resets to try to standardise styling across the modern browsers at the time, these were Internet Explorer, Firefox, and Safari. It did this by removing all margin, padding, borders, and fonts to a common baseline. It could either be included within your own CSS file, or added as a separate CSS file at the start of your CSS in the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; tag. The order is crucial because you&#39;re establishing a standardised foundation. This allows subsequent CSS files to override these resets, either through direct duplication, leveraging the cascade, or by increasing specificity. For example:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/* Basic Reset of the body styling: Specicifity score 0,0,1 */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; system-ui&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #fff&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Here this selector is overriding the one above because it comes after it in the cascade: Specicifity score 0,0,1 */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Comic Sans MS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Impact&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/* Here we are using CSS Specicifity to override the page background color: Specicifity score 0,1,1  */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;body.colored-background&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff0000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is why ordering your CSS correctly is best to do right at the start of a project. If you bring in a reset file at the end, it will either do nothing at all due to higher specificity CSS selectors before it, or it will completely undo lots of your styling, simply because it “wins” (by coming last in the cascade e.g. it’s the last CSS file loaded).&lt;p&gt;As mentioned above, CSS resets have evolved over the years. &lt;a href=&quot;https://necolas.github.io/normalize.css/&quot;&gt;Normalize.css&lt;/a&gt; is a very common one in use on the modern web, as it works differently by preserving useful default browser styles and only fixing CSS styles that need to be fixed to maximise CSS consistency across modern browsers.&lt;p&gt;Other notable mentions are more modern, minimal resets that only focus on certain pain points in cross-browser rendering like box-sizing, responsive images, and font inheritance. These include &lt;a href=&quot;https://piccalil.li/author/andy-bell/&quot;&gt;Andy Bell&#39;s&lt;/a&gt;: &lt;a href=&quot;https://piccalil.li/blog/a-more-modern-css-reset/&quot;&gt;A (more) Modern CSS Reset&lt;/a&gt; and &lt;a href=&quot;https://www.joshwcomeau.com/about-josh/&quot;&gt;Josh Comeau&lt;/a&gt;&#39;s: &lt;a href=&quot;https://www.joshwcomeau.com/css/custom-css-reset/&quot;&gt;A Modern CSS Reset&lt;/a&gt;.&lt;p&gt;Kudos to the authors of CSS Resets! Their dedication makes CSS authoring significantly smoother for millions of developers across the world.&lt;h3 id=&quot;hover-only-interactions&quot;&gt;Hover-Only Interactions&lt;/h3&gt;&lt;p&gt;Hover-only interactions are a legacy practice that suited desktop-only contexts but fail in today’s multi-device environment. An example of what a hover-only Interaction is:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.button:hover&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff0000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hover-only Interactions come with the following issues:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Not accessible on touch devices&lt;/strong&gt;: Touchscreens do not have a hover state. This means hover-only functionality becomes inaccessible on phones and tablets, leading to broken user experiences.&lt;li&gt;&lt;strong&gt;Not accessible on touch devices&lt;/strong&gt;: Touchscreens do not have a hover state. This means hover-only functionality becomes inaccessible on phones and tablets, leading to broken user experiences.&lt;li&gt;&lt;strong&gt;Lack of fallback interaction&lt;/strong&gt;: Many legacy implementations didn&#39;t provide alternative means (like a click or focus) to trigger the same behaviour, effectively hiding essential UI or functionality.&lt;li&gt;&lt;strong&gt;Keyboard accessibility problems&lt;/strong&gt;: Hover interactions are not always accessible via keyboard unless explicitly paired with &lt;code&gt;:focus&lt;/code&gt; or JS handling.&lt;li&gt;&lt;strong&gt;Poor &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;progressive enhancement&lt;/a&gt;&lt;/strong&gt;: Relying solely on hover effects often ignored the principle of progressive enhancement, especially when essential content was hidden using CSS unless hovered.&lt;li&gt;&lt;strong&gt;Inconsistent browser behaviour&lt;/strong&gt;: Legacy browsers had quirks in how they handled hover states, particularly with complex layouts or when mixing JS and CSS interactions.&lt;/ul&gt;&lt;h4 id=&quot;modern-best-practice-2&quot;&gt;Modern Best Practice&lt;/h4&gt;&lt;p&gt;UI&#39;s need to be device-agnostic and align with inclusive design principles. Hover-based interactions should be a &lt;strong&gt;supplementary&lt;/strong&gt; interaction, not the &lt;strong&gt;primary&lt;/strong&gt; interaction. In order to align with modern best practice you should:&lt;ul&gt;&lt;li&gt;Avoid hover-only interactions for essential functionality.&lt;li&gt;Use &lt;code&gt;:focus&lt;/code&gt; alongside &lt;code&gt;:hover&lt;/code&gt;, and consider adding &lt;code&gt;:focus-visible&lt;/code&gt; to &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible&quot;&gt;better support keyboard navigation&lt;/a&gt;.&lt;li&gt;Support click or tap events explicitly for mobile compatibility&lt;li&gt;Provide visible indicators or alternative access methods (e.g. always-visible menus on small screens)&lt;/ul&gt;&lt;p&gt;An example in CSS is:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;.&lt;span class=&quot;token property&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;hover&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* mouse users */&lt;/span&gt;
.&lt;span class=&quot;token property&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;focus&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* element focused via click or tab */&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.button:focus-visible&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* user likely on keybord, or other assistive technology */&lt;/span&gt;
	 background-color&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff0000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The above can be simplified to avoid redundant styling as combining &lt;code&gt;:focus&lt;/code&gt; and &lt;code&gt;:focus-visible&lt;/code&gt; can sometimes cause overlapping or unnecessary duplication of visual effects. The recommended approach is to use the following as it keeps your styling clean and scoped, applying just what’s needed based on the user’s input method:&lt;pre class=&quot;language-css&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.button:hover,
.button:focus-visible&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #ff0000&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By avoiding redundant styling means you reduce:&lt;ul&gt;&lt;li&gt;Overlapping in CSS rules.&lt;li&gt;Maintenance complexity.&lt;li&gt;Risk of inconsistent behaviour between browsers.&lt;li&gt;Slightly less to download.&lt;/ul&gt;&lt;h2 id=&quot;9-legacy-web-strategies&quot;&gt;9. Legacy Web Strategies&lt;/h2&gt;&lt;h3 id=&quot;blackhat-seo&quot;&gt;Blackhat SEO&lt;/h3&gt;&lt;p&gt;Blackhat SEO refers to a collection of techniques that people tried to use to manipulate search engine rankings, mostly in ways that violate search engine guidelines, especially for guidelines laid out by Google.&lt;h4 id=&quot;intent&quot;&gt;Intent&lt;/h4&gt;&lt;p&gt;So why would people want to use Blackhat SEO? Well, its sole focus was prioritising rapid results over sustainable growth. Consultants selling these techniques were focussing on exploiting weaknesses in search engine algorithms, rather than creating genuine value for users. Being at the top of the Google results page was the primary goal, and companies were willing to try these techniques to get an edge on their competition. That was until search engines got wise to what was happening, and started to penalise sites that employed these techniques.&lt;h4 id=&quot;examples&quot;&gt;Examples&lt;/h4&gt;&lt;p&gt;Let&#39;s look over a few outdated examples and how they worked:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Keyword stuffing&lt;/strong&gt;: This was essentially stuffing as many keywords into a page in an attempt to trick the search engine into pushing a page to the top of the results for more search engine searches. So even if the keywords weren&#39;t at all related to the actual content, they were included anyway. Thankfully, search engines got wise to this tactic and cracked down on sites that used it.&lt;li&gt;&lt;strong&gt;Cloaking&lt;/strong&gt;: This is where you show different content to search engines than you do to users. Search engines would be shown pages with detailed, keyword-rich content about a specific product in order to trick a search engine into ranking the page highly. But the page shown to users was minimal and mainly promotional with very little or no helpful information on the page related to what it was being ranked on.&lt;li&gt;&lt;strong&gt;Hidden text and links&lt;/strong&gt;: This is the one I remember the most, using CSS or HTML to hide text on a page that was only intended for search engines. Think white colour text on a white background, it was that simple! It was also straightforward to spot, as you&#39;d get pages where the scrollbar was huge, but the content on the page was very short. The overflow in the vertical direction was the hidden text that you could easily reveal by highlighting the text with your cursor!&lt;li&gt;&lt;strong&gt;Link farms and paid link schemes&lt;/strong&gt;: This is where companies would create hundreds, or thousands of low-quality content linking back to a specific page, in the hope that the search engine would rank the page highly because of all the backlinks to it. There were (and most likely still are) whole business&#39;s setup that promised to get you to the top of search results by essentially spamming the web like this. If you ever had a WordPress blog without the &lt;a href=&quot;https://akismet.com/&quot;&gt;Akismet&lt;/a&gt; plug-in, you&#39;d see this rapidly! WordPress was highly vulnerable due to its support for &lt;a href=&quot;https://en.wikipedia.org/wiki/Trackback&quot;&gt;Trackbacks&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Pingback&quot;&gt;Pingbacks (XML-RPC)&lt;/a&gt;. Have a look at any old unmaintained WordPress blog, and you are likely to see this. They were so common at one point that a new term called &quot;splogs&quot; (spam blogs) was created for them. Wired.com wrote a blog post all about them back in September 2006 &quot;&lt;a href=&quot;https://www.wired.com/2006/09/splogs/&quot;&gt;Spam + Blogs = Trouble&lt;/a&gt;&quot;.&lt;li&gt;&lt;strong&gt;Duplicate content&lt;/strong&gt;: This is a simple strategy, copy another site&#39;s high-quality content and pass it off as your own. Thankfully, this now triggers de-ranking in search engines.&lt;li&gt;&lt;strong&gt;Automated content&lt;/strong&gt;: Basically automating the production of low-quality and spammy content. I anticipate that this technique will see a resurgence, given the recent surge in AI tools.&lt;/ul&gt;&lt;p&gt;&quot;This is why we can&#39;t have nice things!&quot; The phrase echoes in my ears as I recall all the techniques mentioned above.&lt;h4 id=&quot;why-are-they-outdated&quot;&gt;Why are they Outdated?&lt;/h4&gt;&lt;p&gt;There are a number of reasons why these techniques are no longer used:&lt;ol&gt;&lt;li&gt;Google and other search engines have significantly enhanced their algorithms to identify and penalise such manipulative tactics.&lt;li&gt;Modern SEO is more geared towards user-first metrics, content relevance, quality, user experience, and even &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/seo/meta-description&quot;&gt;web performance&lt;/a&gt; are now taken into account when ranking a web page.&lt;li&gt;Sites found to be using these Blackhat tactics are almost certain to be heavy penalised and may even be de-indexed completely from search engines.&lt;li&gt;Companies found to be using these tactics on the modern web are very likely to suffer reputational and credibility damage. Some sectors that are heavily regulated will likely have legal implications too.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-33&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;Fortunately, these blackhat techniques are no longer effective on the web. They are detrimental to both users and the internet, yet some individuals persist in attempting to use them.&lt;p&gt;For example, Google now ranks web pages on their usability issues, one of these is called &lt;a href=&quot;https://web.dev/articles/cls&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;. This metric measures the stability of a page while a website is loading, websites that &quot;shift around&quot; while loading (called &quot;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Jank&quot;&gt;jank&lt;/a&gt;&quot;) aren&#39;t scored as highly as those that are more stable.&lt;p&gt;I recently saw a new SEO technique using JS that would mask an entire page with a transparent element in order to trick the browsers CLS metric into thinking the page was completely stable. After page load this element would be deleted and the page could be interacted as usual. Basically, a modern day cloaking technique aimed at improving a pages &lt;a href=&quot;https://wicg.github.io/layout-instability/&quot;&gt;Layout Instability API&lt;/a&gt; score.&lt;p&gt;So yes, it still happens and &quot;this is why we &lt;strong&gt;still&lt;/strong&gt; can&#39;t have nice things!&quot;.&lt;h3 id=&quot;above-the-fold-obsession&quot;&gt;“Above the Fold” obsession&lt;/h3&gt;&lt;p&gt;The concept of &quot;above the fold&quot; is an outdated technique. The fold&#39;s position is not fixed; rather, it varies depending on the device used to view a web page. If we take this to the extreme. Viewing a website on a desktop widescreen device vs a mobile device, there&#39;s never going to be a common &quot;fold&quot; in this situation. Consider the vast number of device widths, ranging from a large desktop widescreen to a mobile device—literally thousands along the x-axis. If you then factor in the viewport height (y-axis), you&#39;re looking at millions of possible viewport permutations. Past assumptions are no longer true:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;User behaviour&lt;/strong&gt;: Users scroll instinctively now. The old belief that users don’t scroll is no longer valid.&lt;li&gt;&lt;strong&gt;Web performance evolution&lt;/strong&gt;: Modern performance metrics (like &lt;a href=&quot;https://web.dev/articles/lcp&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt; and &lt;a href=&quot;https://web.dev/articles/inp&quot;&gt;Interaction to Next Paint (INP)&lt;/a&gt;) reward real user-perceived speed, not just fast above-the-fold content.&lt;li&gt;&lt;strong&gt;Lazy loading and streaming&lt;/strong&gt;: The web has moved towards prioritising meaningful content dynamically, rather than front-loading everything visible “above the fold”.&lt;/ol&gt;&lt;h4 id=&quot;can-i-still-use-it-34&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;It depends. “Above the fold optimisation” is an older performance technique that focuses on rendering the visible portion of the page as quickly as possible. When used thoughtfully, it can still improve perceived load speed, especially in critical user flows. However, relying on it too heavily can narrow the focus to just a fragment of the overall experience.&lt;p&gt;Today, the more effective and sustainable approach is to optimise for end-to-end, user-centric performance. This includes not only what appears first on-screen, but also how quickly the page becomes usable and interactive. A strategy focused on delivering a consistently fast page experience will naturally improve the content visible without scrolling, regardless of the device.&lt;h3 id=&quot;superseded-compatibility-approaches&quot;&gt;Superseded compatibility approaches&lt;/h3&gt;&lt;h4 id=&quot;graceful-degradation&quot;&gt;Graceful Degradation&lt;/h4&gt;&lt;p&gt;The technique of graceful degradation involves building a website to take advantage of all the modern features of a browser, and once completed add &quot;fall backs&quot; for browsers that don&#39;t support modern features.&lt;h5 id=&quot;examples-2&quot;&gt;Examples&lt;/h5&gt;&lt;p&gt;An example of graceful degradation is: a developer builds a website where the main layout is using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/grid&quot;&gt;CSS Grid&lt;/a&gt;. But if a browser doesn&#39;t support Grid it will &quot;fall back&quot; to a simpler layout system like &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox&quot;&gt;Flexbox&lt;/a&gt; or even a &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Floats&quot;&gt;float-based layout&lt;/a&gt; (depending on the site&#39;s browser support requirements).&lt;p&gt;Another example is a feature-rich, JS-enhanced input form may fall back to a basic HTML form if JS is disabled or &lt;a href=&quot;https://www.kryogenix.org/code/browser/why-availability/&quot;&gt;fails to load&lt;/a&gt;, for example, due to a poor or unstable network connection. In this case, core functionality (such as form submission) remains available, even though advanced features (like real-time validation or dynamic UI elements) are unavailable.&lt;h5 id=&quot;why-is-it-legacy&quot;&gt;Why is it Legacy?&lt;/h5&gt;&lt;p&gt;Graceful degradation is increasingly being considered a legacy approach in modern web development as it has largely been superseded by &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;progressive enhancement&lt;/a&gt; which takes the opposite approach.&lt;h5 id=&quot;why-is-it-outdated-6&quot;&gt;Why is it outdated?&lt;/h5&gt;&lt;p&gt;This assumption of a modern baseline fails to acknowledge the true diversity of browsers and devices currently in use by users. Furthermore, comprehensive testing is challenging due to the difficulty of covering all scenarios involving older or limited browsers. Third, adding &quot;fallbacks&quot; further increases the complexity of an already intricate full-featured initial build. Most importantly, Graceful Degradation negatively impacts accessibility and resilience. Pages employing this technique frequently fail in low-capability environments, such as older browsers, devices, or poor connections.&lt;h5 id=&quot;can-i-still-use-it-35&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;There are a few scenarios where it may still be useful, these include:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Legacy enterprise environments&lt;/strong&gt;: for example a company that mandates the use of older browsers like Internet Explorer. A notable example of this is &lt;a href=&quot;https://web.archive.org/web/20220708090533/http://www.nytimes.com/2022/07/08/business/korea-internet-explorer.html/&quot;&gt;Banks and other financial institutions in South Korea&lt;/a&gt;. For a country, that &lt;a href=&quot;https://explodingtopics.com/blog/countries-internet-users#internet-adoption-statistics-by-nation&quot;&gt;ranks 12th on the internet adoption rate for its citizens&lt;/a&gt; (97.4% it 2025), it&#39;s a pretty surprising legacy issue they are still trying to tackle!&lt;li&gt;&lt;strong&gt;Modernisation&lt;/strong&gt;: If a website is in a transition phase of being modernised, and it still needs to support older browsers for a limited period.&lt;li&gt;&lt;strong&gt;Non-critical enhancements&lt;/strong&gt;: If a site has non-critical enhancements like animations or media features that are optional and don&#39;t impact access to the site&#39;s core content.&lt;/ul&gt;&lt;h5 id=&quot;what-should-i-use-instead&quot;&gt;What should I use instead?&lt;/h5&gt;&lt;p&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;Progressive Enhancement&lt;/a&gt; is now the preferred approach for modern web development, offering a more robust, inclusive, and future-proof way to build websites and web applications. While Graceful Degradation was a useful technique for older browsers, it has now been superseded.&lt;h4 id=&quot;browser-sniffing&quot;&gt;Browser Sniffing&lt;/h4&gt;&lt;p&gt;This is the practice of detecting more information about a user&#39;s browser, like its specific version number or the operating system it is currently running on. Once detected a developer can use this information to “fork” their code, e.g. make decisions as to what bug workarounds should, or shouldn&#39;t be applied. Or even tailor the user experience for a specific version of a browser. Two very common uses of this technique in the past were to redirect a user to the mobile version of the site (when mobile and desktop sites built separately), or even blocking the usage of a site on &quot;unsupported&quot; browsers. An example of how you&#39;d do this in JS is below:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Chrome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Apply Chrome-specific behaviour or simply block other browsers if you are feeling malicious&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code highlights a significant problem with browser sniffing, demonstrating why it&#39;s considered an outdated technique. In the code, the whole functionality is decided by the fact that the User-Agent variable in the browser happens to include the string &quot;Chrome&quot;. But what happens if Google one day decides to change this string to lowercase &quot;chrome&quot;, or even change it completely? Well, the code depending on this detection will break!&lt;p&gt;Now, you could modify the above code to tackle the case issue like so:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userAgent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLowerCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;chrome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Apply Chrome-specific behaviour&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But as you can see, this has only made the code more &lt;strong&gt;complex&lt;/strong&gt; and &lt;strong&gt;fragile&lt;/strong&gt;.&lt;p&gt;It&#39;s also worth mentioning that this code won&#39;t do what you expect it to either, as all Chrome based browser will return true. For example:&lt;ul&gt;&lt;li&gt;Google Chrome&lt;li&gt;Microsoft Edge (Chromium-based)&lt;li&gt;Opera (also Chromium-based)&lt;li&gt;Brave&lt;li&gt;Vivaldi&lt;/ul&gt;&lt;p&gt;At the time of writing each of the User-Agent strings for the above browsers contain: &lt;code&gt;Chrome/115.0.0.0&lt;/code&gt; (as well as other information, that I have removed for the example).&lt;p&gt;All contain &quot;Chrome&quot; in their User-Agent, so will all run the code.&lt;p&gt;What&#39;s worse is that Chrome on iOS will return &lt;code&gt;false&lt;/code&gt; and &lt;strong&gt;not&lt;/strong&gt; run the code. On iOS, all browsers, including what appears to be Chrome on the home screen, are actually forced to use WebKit (Safari). Consequently, &quot;Chrome&quot; in this instance isn&#39;t truly Chrome and is not reflected in the User-Agent String.&lt;h5 id=&quot;other-issues&quot;&gt;Other issues&lt;/h5&gt;&lt;p&gt;Fragility isn&#39;t the only issue seen when using this technique. It can also:&lt;ul&gt;&lt;li&gt;add a maintenance burden for developers, as this logic will need to be updated as browsers evolve.&lt;li&gt;create browser feature mismatch, two versions of the same browser don&#39;t always support the same features.&lt;li&gt;cause accessibility risks leading to user exclusion. A user on a less-common browser or assistive technologies, could inadvertently receive a degraded experience, or even blocked completely.&lt;/ul&gt;&lt;h5 id=&quot;can-i-still-use-it-36&quot;&gt;Can I still use it?&lt;/h5&gt;&lt;p&gt;Realistically, no, you should aim to avoid browser sniffing entirely and instead of asking &lt;em&gt;which browser&lt;/em&gt; a user is using, ask &lt;em&gt;what can their browser do?&lt;/em&gt; Essentially, you want to be detecting the features that the user&#39;s browser can support. For example, to detect if a browser supports the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API&quot;&gt;Service Worker API&lt;/a&gt;, you can do this:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;serviceWorker&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// The users browser supports the &#39;serviceWorker&#39; API, so do Service Worker stuff!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h5 id=&quot;browser-sniffing-summary&quot;&gt;Browser Sniffing Summary&lt;/h5&gt;&lt;p&gt;In summary, browser sniffing is a legacy technique that should be avoided on the modern web. In order to create a more resilient and inclusive web, you should use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Testing/Feature_detection&quot;&gt;Feature Detection&lt;/a&gt;, &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#graceful-degradation&quot;&gt;Graceful Degradation&lt;/a&gt;, and &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#11-what-still-matters-progressive-enhancement&quot;&gt;Progressive Enhancement&lt;/a&gt; instead.&lt;h3 id=&quot;modernizr&quot;&gt;Modernizr&lt;/h3&gt;&lt;p&gt;I was a big fan of &lt;a href=&quot;https://github.com/Modernizr/Modernizr&quot;&gt;Modernizr&lt;/a&gt; (with its very Web 2.0 name!). For readers who&#39;ve not used or heard of it, Modernizr is a HTML5 and CSS3 feature detection library, It does this via browser feature detection, rather than browser user-agent strings, which can be unreliable and misleading. Modernizr actually tests to see if the browser being used supports a whole host of features.&lt;p&gt;It was released in 2009 at version 1.0, since then, it has had &lt;a href=&quot;https://github.com/Modernizr/Modernizr/releases&quot;&gt;27 releases&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Modernizr/Modernizr/graphs/contributors&quot;&gt;300 contributors&lt;/a&gt;. So how does it work, and how exactly do you use it? Here&#39;s an example of how it detects &lt;code&gt;flexbox&lt;/code&gt; support in a user&#39;s browser.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Adds a new test to the Modernizr object under the key &#39;flexbox&#39;&lt;/span&gt;
Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;flexbox&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Create a new HTML div element to test CSS properties on&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; testElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;div&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Attempt to set the display property to &#39;flex&#39;&lt;/span&gt;
  testElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Check if the browser retains the value &#39;flex&#39; for the display property&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// If supported, the style will remain &#39;flex&#39;; otherwise, it may remain empty or be changed&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; testElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;display &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;flex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here&#39;s how you would use that in your website. There are 2 methods for how you use it:&lt;h4 id=&quot;css&quot;&gt;CSS&lt;/h4&gt;&lt;p&gt;Modernizr adds a class to the &lt;code&gt;&amp;lt;html&gt;&lt;/code&gt; element, in our case above it would be:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;flexbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- If the browser supports it. --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no-flexbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &amp;lt;!-- If the browser doesn&#39;t supports it.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Thus, you could hang any styles you needed off these classes and tweak the CSS layout for both scenarios, flex support and no flex support.&lt;h4 id=&quot;javascript&quot;&gt;JavaScript&lt;/h4&gt;&lt;p&gt;The other method is to use it in JS by examining the Modernizr object:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Modernizr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;flexbox&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Flexbox is supported, do Flexbox JS stuff here&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Flexbox is not supported, no non Flexbox JS stuff here&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The great thing about Modernizr is that even thought it supports over 250 feature tests for HTML5, CSS3, and JS APIs, it allows developers to create custom-builds that &lt;strong&gt;only&lt;/strong&gt; detect the features they intend to use. This improves performance in 2 ways:&lt;ol&gt;&lt;li&gt;Less JS to download, parse, then execute.&lt;li&gt;Fewer bytes sent over the network.&lt;/ol&gt;&lt;h4 id=&quot;use-cases&quot;&gt;Use cases:&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;Applying fallback CSS or JS for unsupported features.&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement&quot;&gt;Progressive enhancement&lt;/a&gt; strategies.&lt;li&gt;Conditional &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Polyfill&quot;&gt;polyfill&lt;/a&gt; loading.&lt;li&gt;Responsive design tweaks based on feature support (via JS)&lt;/ul&gt;&lt;h4 id=&quot;can-i-still-use-it-37&quot;&gt;Can I still use it?&lt;/h4&gt;&lt;p&gt;It&#39;s still useful, but it isn&#39;t as essential as it once was, this is because:&lt;ul&gt;&lt;li&gt;Browser standardisation has vastly improved since 2009.&lt;li&gt;Native CSS now support &lt;a href=&quot;https://caniuse.com/css-featurequeries&quot;&gt;feature queries&lt;/a&gt; (&lt;code&gt;@supports&lt;/code&gt;).&lt;li&gt;Feature detection is often built into some JS frameworks.&lt;li&gt;Support for ancient browsers has declined.&lt;/ul&gt;&lt;p&gt;Ironically, Modernizr, despite detecting over 250 features, does not detect JS support itself. This isn&#39;t a problem, though, because if you require this functionality it can be added via a single line of JS in the &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; of your page:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Default setup --&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;no-js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
    document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;documentElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;className&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;no-js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By default, the script assumes that the browser doesn&#39;t support JS (&lt;code&gt;class=&quot;no-js&quot;&lt;/code&gt;). When it gets to the inline script tag, the script executes. And as this proves that JS is supported it swaps the &lt;code&gt;no-js&lt;/code&gt; class to a &lt;code&gt;js&lt;/code&gt; class that can then be used in CSS styling, as you would any other Modernizr CSS class as demonstrated above.&lt;h2 id=&quot;10-tests-and-standards-of-yesteryear&quot;&gt;10. Tests and Standards of Yesteryear&lt;/h2&gt;&lt;h3 id=&quot;acid2-and-acid3-tests&quot;&gt;Acid2 and Acid3 Tests&lt;/h3&gt;&lt;p&gt;The Acid2 and Acid3 tests were really clever ways to test a browser&#39;s compliance with the ever-evolving rendering standards at the time. They were both created by the &lt;a href=&quot;https://www.webstandards.org/&quot;&gt;Web Standards Project (WaSP)&lt;/a&gt;which was founded in 1998, when the web was a battleground of two main browsers:&lt;ol&gt;&lt;li&gt;Microsoft (Internet Explorer 4 at the time)&lt;li&gt;Netscape (Netscape Navigator 4.05 at the time)&lt;/ol&gt;&lt;p&gt;The Web Standards Project, aimed to promote web standards that made development simpler, more accessible, and future-proofed, by working closely with browser vendors and development toolmakers to achieve this. When the team posted their &lt;a href=&quot;https://www.webstandards.org/2013/03/01/our-work-here-is-done/index.html&quot;&gt;final blog post in March 2013&lt;/a&gt;, their mission was largely complete. It resulted in successfully getting browser vendors to support the standards set by the &lt;a href=&quot;https://www.w3.org/&quot;&gt;World Wide Web Consortium (W3C)&lt;/a&gt;. As of 2025, the W3C remains active in setting new standards to ensure the web continues to support communication, commerce, and knowledge-sharing for all, with a strong focus on accessibility, diversity, and inclusion.&lt;h4 id=&quot;acid2-2005&quot;&gt;Acid2 (2005)&lt;/h4&gt;&lt;p&gt;The &lt;a href=&quot;http://acid2.acidtests.org/&quot;&gt;Acid2 test&lt;/a&gt; was created to test compliance with HTML 4.01, CSS 1 &amp; 2, and PNG rendering standards (e.g. alpha transparency). It did this by focussing on the following key areas of browser rendering:&lt;ul&gt;&lt;li&gt;Box model&lt;li&gt;Absolute and relative positioning&lt;li&gt;Float behaviour&lt;li&gt;Table layout&lt;li&gt;PNG alpha transparency&lt;li&gt;Data URLs&lt;/ul&gt;&lt;p&gt;This was achieved through a highly innovative browser test: creating a simple cartoon face, similar to a smiling emoji. Depending on how compliant the browser was, influenced how well the face was rendered. It&#39;s much simpler if I just show you!&lt;h5 id=&quot;rendering-reference&quot;&gt;Rendering reference&lt;/h5&gt;&lt;p&gt;This is what the output of the test is supposed to look like across &lt;strong&gt;all browsers&lt;/strong&gt;.&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-628.webp 628w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;A picture of a yellow pixelated smiling face with green eyes to indicate that the browser was compliant with the rendering standards of the day.&quot; decoding=&quot;async&quot; height=&quot;632&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/GMrcbbNVXJ-628.png 628w&quot; width=&quot;628&quot;&gt;&lt;/picture&gt;&lt;h5 id=&quot;internet-explorer-6&quot;&gt;Internet Explorer 6&lt;/h5&gt;&lt;p&gt;This &quot;face,&quot; rendered with IE6, appears as if the individual suffered a severe accident!&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/xVLfj2QpPx-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/xVLfj2QpPx-546.webp 546w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;A version of the face rendered in IE6, it&#39;s basically a big red box with no discernible facial features at all!&quot; decoding=&quot;async&quot; height=&quot;618&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/xVLfj2QpPx-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/xVLfj2QpPx-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/xVLfj2QpPx-546.png 546w&quot; width=&quot;546&quot;&gt;&lt;/picture&gt;&lt;h5 id=&quot;netscape-4-8&quot;&gt;Netscape 4.8&lt;/h5&gt;&lt;p&gt;Netscape performed similarly to IE6 at the time.&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-605.webp 605w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Another face accident rendered by Netscape 4.8 a big red box surrounded by a solid black border and broken image icon in the bottom left corner!&quot; decoding=&quot;async&quot; height=&quot;710&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/3hM2Dr7q53-605.png 605w&quot; width=&quot;605&quot;&gt;&lt;/picture&gt;&lt;h5 id=&quot;mozilla-deer-park-alpha-2-later-firefox-3&quot;&gt;Mozilla Deer Park Alpha 2 (later Firefox 3)&lt;/h5&gt;&lt;p&gt;Finally a &lt;a href=&quot;https://www-archive.mozilla.org/projects/deerpark/releases/alpha1.html&quot;&gt;browser&lt;/a&gt; that actually rendered a face!&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-626.webp 626w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Image of a browser window it the face finally looking like the reference image shown on the test site.&quot; decoding=&quot;async&quot; height=&quot;436&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/XY-h1FMrnC-626.png 626w&quot; width=&quot;626&quot;&gt;&lt;/picture&gt;&lt;p&gt;There are far too many variations and versions of browsers around at the time to list here, but if you are interested in how other browsers rendered the Acid2 test check out this &lt;a href=&quot;https://www.howtocreate.co.uk/acid/&quot;&gt;ancient blog post&lt;/a&gt; by author &lt;a href=&quot;https://www.howtocreate.co.uk/bio.html&quot;&gt;Mark &quot;Tarquin&quot; Wilton-Jones&lt;/a&gt;. Mark, thank you for safeguarding this significant and captivating piece of web standards history!&lt;h4 id=&quot;acid3-2008&quot;&gt;Acid3 (2008)&lt;/h4&gt;&lt;p&gt;After the success of the Acid2 browser test in putting pressure on browser vendors to improve standards support in their browsers, it was decided by the WaSP team to create another browser test, this time focussing on a different set of browser technologies. These included:&lt;ul&gt;&lt;li&gt;DOM Level 2 and 3&lt;li&gt;ECMAScript (JS) behaviour&lt;li&gt;CSS 3 selectors&lt;li&gt;SVG rendering&lt;li&gt;Data URIs&lt;li&gt;Animation timing and rendering&lt;li&gt;WebFonts via &lt;code&gt;@font-face&lt;/code&gt;&lt;/ul&gt;&lt;h5 id=&quot;rendering-reference-2&quot;&gt;Rendering reference&lt;/h5&gt;&lt;p&gt;The &lt;a href=&quot;http://acid3.acidtests.org/&quot;&gt;Acid3 test&lt;/a&gt; took a more traditional testing route and simply scored a browser taking the test between 1 (poor support) and 100 (perfect support).&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-814.webp 814w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-1200.webp 1200w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-1258.webp 1258w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;The Acid3 test comprised of a set of 6 rainbow coloured boxes (Red to Indigo). The score out of 100 was shown directly below these coloured boxes&quot; decoding=&quot;async&quot; height=&quot;840&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-814.png 814w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-1200.png 1200w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/8bKdUgCdEj-1258.png 1258w&quot; width=&quot;1258&quot;&gt;&lt;/picture&gt;&lt;p&gt;In my opinion, the Acid3 test, while practical and easy to interpret, lacked the entertainment value of the Acid2 facial disfigurement test!&lt;p&gt;The release of the more challenging Acid3 test coincided with a surge in browser competition, particularly among Firefox, Safari, Opera, and Google Chrome (which was released later in 2008).&lt;h5 id=&quot;scores&quot;&gt;Scores&lt;/h5&gt;&lt;p&gt;At the time of release (March 2008) the Acid3 scores for each major browser were as follows:&lt;ul&gt;&lt;li&gt;&lt;strong&gt;IE7&lt;/strong&gt;: 12 / 100.&lt;li&gt;&lt;strong&gt;IE8 Beta 1&lt;/strong&gt;: 18 / 100.&lt;li&gt;&lt;strong&gt;Firefox 2&lt;/strong&gt;: 50 / 100.&lt;li&gt;&lt;strong&gt;Firefox 3 Beta 4&lt;/strong&gt;: 71 / 100.&lt;li&gt;&lt;strong&gt;Opera 9.5 Beta&lt;/strong&gt;: between 60–70 / 100.&lt;li&gt;&lt;strong&gt;Safari 3.1&lt;/strong&gt;: between 75–90 / 100.&lt;li&gt;&lt;strong&gt;Google Chrome&lt;/strong&gt;: Not yet released in March 2008.&lt;li&gt;&lt;strong&gt;Google Chrome 0.2 Beta: first release&lt;/strong&gt;: 77 / 100.&lt;li&gt;&lt;strong&gt;Google Chrome 1.0&lt;/strong&gt;: 100 / 100.&lt;/ul&gt;&lt;p&gt;Google Chrome quickly improved its Acid3 score shortly after its initial release. This rapid improvement was mainly due to its use of the Safari WebKit engine, which already scored 75–90 out of 100 at the time.&lt;h4 id=&quot;legacy&quot;&gt;Legacy&lt;/h4&gt;&lt;p&gt;Neither test is maintained or relevant to the modern web, but they played a key role in pushing browser vendors toward better standards support. Today, browser compliance is measured using the &lt;a href=&quot;https://web-platform-tests.org/&quot;&gt;Web Platform Tests (WPT)&lt;/a&gt;, a much broader and actively maintained suite developed by the vendors themselves with input from &lt;a href=&quot;https://whatwg.org/&quot;&gt;WHATWG&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/&quot;&gt;W3C&lt;/a&gt;.&lt;h2 id=&quot;11-what-still-matters-progressive-enhancement&quot;&gt;11. What Still Matters – Progressive Enhancement&lt;/h2&gt;&lt;h3 id=&quot;not-legacy-but-often-forgotten&quot;&gt;Not legacy but often forgotten&lt;/h3&gt;&lt;p&gt;Congratulations! You&#39;ve made it! After discussing countless legacy approaches and techniques best left in the past, you&#39;ve finally arrived at a truly timeless and Incredibly important methodology. More than two decades after &lt;a href=&quot;https://www.linkedin.com/in/schampeo/&quot;&gt;Steve Champeon&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Nick_Finck&quot;&gt;Nick Finck&lt;/a&gt; introduced it in their talk &quot;&lt;a href=&quot;https://hesketh.com/publications/inclusive_web_design_for_the_future/&quot;&gt;Inclusive Web Design For the Future&lt;/a&gt;” at &lt;a href=&quot;https://www.sxsw.com/iconicmoments/2003/&quot;&gt;SXSW in 2003&lt;/a&gt;. The Progressive Enhancement (PE) methodology remains one of the most robust and future-ready methods for modern web development.&lt;h4 id=&quot;what-is-progressive-enhancement&quot;&gt;What is Progressive Enhancement?&lt;/h4&gt;&lt;p&gt;There&#39;s a ubiquitous diagram that is always shown whenever PE is mentioned in a blog post. And this blog post will be no different in using it, as it actually explains the concept incredibly well.&lt;p&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-300.webp 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-600.webp 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-800.webp 800w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;This is the progressive enhancement pyramid, HTML is the bottom layer, CSS the 2nd layer, and lastly JavaScript is at the top of the pyramid.&quot; decoding=&quot;async&quot; height=&quot;694&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-300.png 300w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-600.png 600w, https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/SNN9uz1NBR-800.png 800w&quot; width=&quot;800&quot;&gt;&lt;/picture&gt;&lt;p&gt;Here we have the well known Progressive Enhancement pyramid.&lt;h4 id=&quot;html&quot;&gt;HTML&lt;/h4&gt;&lt;p&gt;The HTML is at the bottom of the pyramid as it gives the website a solid foundation on which to build off. The HTML layer is the most resilient layer in the web development stack. Without the HTML there is no content, no links, no images, no website! This layer is by far the most important layer in the pyramid. Just to give you an idea of how resilient HTML is in web browsers, let&#39;s take a look at the &lt;a href=&quot;https://info.cern.ch/hypertext/WWW/TheProject.html&quot;&gt;very first website&lt;/a&gt;. Back on Tuesday, August 6, 1991, &lt;a href=&quot;https://en.wikipedia.org/wiki/Tim_Berners-Lee&quot;&gt;Sir Tim Berners-Lee&lt;/a&gt;, the inventor of the Web, published the very first website! Now, this statistic makes me feel ancient! The first website was published almost 34-years ago, at the time of writing! And what do you notice about the page? Well, most importantly, it still renders correctly, and the content can still be read perfectly well after all this time. If you take a peak at the page&#39;s source code, you will notice a few oddities, like:&lt;ul&gt;&lt;li&gt;The complete lack of a DOCTYPE, and no &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; tag.&lt;li&gt;No links to external stylesheets or JavaScript (they weren&#39;t invented yet!)&lt;li&gt;Anchors existed to link to other pages, but they had a strange &lt;code&gt;NAME=[integer]&lt;/code&gt; attribute.&lt;li&gt;All elements were written in uppercase, e.g. &lt;code&gt;&amp;lt;HEADER&gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;BODY&gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;TITLE&gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;H1&gt;&lt;/code&gt;.&lt;li&gt;Lack of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/HTML#good_semantics&quot;&gt;Semantic Markup&lt;/a&gt;. This was to come later once the WWW had matured.&lt;/ul&gt;&lt;p&gt;To put it into perspective, this website has been around longer than nearly &lt;a href=&quot;https://en.wikipedia.org/wiki/Demographics_of_the_world&quot;&gt;half of the entire global population&lt;/a&gt;, that’s over &lt;strong&gt;4 billion people younger&lt;/strong&gt; than this single page on the internet! Which other digital format on the planet can boast that form of robustness and ease of accessibility? Just think about all the storage media formats that have come and gone in that time:&lt;ul&gt;&lt;li&gt;5.25-inch floppy disk (the disk that &lt;em&gt;was&lt;/em&gt; actually floppy)&lt;li&gt;3.5-inch floppy disk (the 3D &quot;save icon&quot;)&lt;li&gt;CD-ROM / CD-R / CD-RW&lt;li&gt;MiniDisc (data version)&lt;li&gt;CompactFlash (CF)&lt;li&gt;Zip disk&lt;li&gt;Jazz drive&lt;li&gt;DVD-ROM / DVD±R / DVD±RW&lt;li&gt;Blu-ray&lt;li&gt;HD DVD&lt;/ul&gt;&lt;p&gt;The list goes on… The point is clear: if you need a long-term, reliable storage solution that just works, plain HTML on the web is hard to beat! FYI: Of course, these web assets ultimately reside on physical hardware in data centres, but that’s not the point. What matters is the resilience and accessibility the web platform offers, regardless of the underlying infrastructure.&lt;h4 id=&quot;css-2&quot;&gt;CSS&lt;/h4&gt;&lt;p&gt;The second layer in the pyramid is the CSS or the Cascading Style Sheets. When the World Wide Web (W3) was first invented back in the early 90s, CSS simply didn&#39;t exist. It wasn&#39;t until 1996 / mid-1997 that browsers started to support the &lt;a href=&quot;https://www.w3.org/TR/CSS1/&quot;&gt;CSS Level 1 Specification&lt;/a&gt;. The browsers at the time were Internet Explorer 3 and Netscape Navigator 4, both had partial (and mostly buggy implementations). Up until this point the web had been completely &quot;naked&quot; in terms of design. Just pages full of text, images, and the odd animated GIF. Nothing at all like the modern web we see today.&lt;p&gt;CSS constitutes the second layer of the pyramid because, frankly, it is a &quot;nice to have.&quot; Browsers are equipped with default stylesheets (as previously discussed in the &lt;a href=&quot;https://nooshu.com/blog/2025/08/26/hack-to-the-future-frontend/#css-resets&quot;&gt;CSS Resets section&lt;/a&gt;), which enable HTML content to display correctly and remain readable even in the absence of a website&#39;s custom styles. A company or brand must ensure their CSS is available for browsers to download so that their website renders correctly. Without it, many users would assume the site is broken, especially given how modern websites are expected to appear. But in the unlikely event the CSS fails to load, users will still receive the HTML content in a perfectly readable format. While it may appear unappealing, it remains fully functional across all current (and all future) web browsers and assistive technologies.&lt;p&gt;The beauty of Progressive Enhancement lies in establishing a foundational layer (HTML) and then &lt;strong&gt;progressively&lt;/strong&gt; adding desired features. This method ensures that if any subsequent layer fails, the underlying content and functionality remain accessible to users.&lt;p&gt;A prime example of this browser feature in action is, since April 9, 2006, &lt;a href=&quot;https://css-naked-day.org&quot;&gt;CSS Naked Day&lt;/a&gt; has been observed. Where for a &lt;a href=&quot;https://css-naked-day.org/#timespan&quot;&gt;50-hour period&lt;/a&gt;, website owners disable their site&#39;s CSS, allowing users to experience the semantic HTML without styling. It started as a push for web standards and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/HTML#good_semantics&quot;&gt;semantic markup&lt;/a&gt;, and gave site owners an excuse to flaunt their sexy &lt;code&gt;&amp;lt;body&gt;&lt;/code&gt;. Gotta love a good HTML pun!&lt;h4 id=&quot;javascript-2&quot;&gt;JavaScript&lt;/h4&gt;&lt;p&gt;The final layer of the pyramid and the final piece of the Web stack puzzle is JS, this is the interaction layer that is added to a site last after the foundation (HTML) and design (CSS) have been added. It&#39;s difficult to believe that just &lt;strong&gt;three&lt;/strong&gt; technologies, all of which have been discussed in this section, form the entirety of the web. There is truly nothing more to it than these three foundational components. Ultimately, the output of both frontend and backend development invariably consists of standard HTML, CSS, and JS. Although a multitude of tools and languages are available for web developers to use, with endless paths to choose from, they eventually all lead to identical HTML, CSS, and JS as their final output. It all comes down to using the right tool and technology for the job!&lt;p&gt;JS is deliberately placed as the final enhancement layer in the pyramid. This is not incidental. JS, while powerful, is the least resilient layer in the web stack. Its execution depends on multiple fragile components:&lt;ul&gt;&lt;li&gt;the network&lt;li&gt;the parser&lt;li&gt;the runtime environment&lt;li&gt;the integrity of the code itself.&lt;/ul&gt;&lt;p&gt;A single misplaced character, for example, an errant semicolon or an &lt;code&gt;undefined&lt;/code&gt; variable, can render entire swathes of interaction inoperable. This fragility is not a hypothetical risk. It manifests regularly across production environments all over the web, particularly where sites are heavily reliant on client-side code for core user journeys.&lt;h4 id=&quot;progressive-enhancement-summary&quot;&gt;Progressive Enhancement Summary&lt;/h4&gt;&lt;p&gt;The modern web has increasingly drifted away from the principles of Progressive Enhancement, often placing JS as the foundation rather than the finishing touch. Single Page Applications are a prime example, where even basic navigation and content rendering require full JS execution. This inversion of the pyramid not only risks total inoperability in degraded environments but also introduces avoidable accessibility and performance issues.&lt;p&gt;From a resilience and user experience standpoint, over-reliance on JS creates brittleness. Unlike HTML and CSS, which both degrade gracefully, JS fails noisily and catastrophically. If a CSS file fails to load, a page might look plain but will still remain usable. If a JS bundle fails, the entirety of the website&#39;s features may be lost, with little to no fallback available.&lt;p&gt;The web’s reach includes users with:&lt;ul&gt;&lt;li&gt;unreliable networks&lt;li&gt;older devices&lt;li&gt;constrained data plans&lt;li&gt;assistive technologies&lt;/ul&gt;&lt;p&gt;A heavy dependence on JS frequently excludes these users or significantly worsens their experience. Progressive Enhancement is not about supporting “no JavaScript” users as a niche edge case. It’s about ensuring a robust baseline that works for everyone, every time, demonstrating empathy for all users regardless of how they access the internet.&lt;p&gt;While JS &lt;em&gt;is&lt;/em&gt; a vital tool in a web developer’s toolkit, it must be handled with care. Its position at the top of the Progressive Enhancement pyramid reflects its power, but also its fragility. It should be used responsibly, with the awareness that its failure often leads to a broken experience. True resilience comes from building upwards from stable foundations, not downwards from brittle interactions.&lt;h3 id=&quot;importance-in-government-services&quot;&gt;Importance in government services&lt;/h3&gt;&lt;p&gt;Having worked at GDS for 6-years, I can&#39;t tell you how many times I had to defend the frontend communities stance on Progressive Enhancement! Thankfully, it&#39;s all written in black and white in the &lt;a href=&quot;https://www.gov.uk/service-manual/technology/using-progressive-enhancement&quot;&gt;Service Manual for all to read&lt;/a&gt;. However, some departments and developers found ways to work around the methodology or opted for alternative approaches. This was most likely driven by two things:&lt;ol&gt;&lt;li&gt;A team had made significant progress with their JS dependent service and were expressing concerns about meeting the requirements for their future &lt;a href=&quot;https://www.gov.uk/service-manual/service-assessments&quot;&gt;service assessment(s)&lt;/a&gt;.&lt;li&gt;Some Frontend Developers in the department were enthusiastic about adopting the latest client-side frameworks, with less emphasis on assessing their maturity or suitability for the service, and its users.&lt;/ol&gt;&lt;p&gt;For point 1 it always amazed me that teams were able to get so far into prototyping for it to become an issue. As depressing as it may be to me, maybe the Service Manual and its guidance isn&#39;t as well-known across government as I&#39;d like to believe?&lt;p&gt;For point 2, I 100% get it, new technology on the web is fun to play with and also to have on your CV / Resume! The real question is whether this &lt;strong&gt;new technology&lt;/strong&gt; is truly the right choice for a critical public service that &lt;strong&gt;every&lt;/strong&gt; UK taxpayer depends on and has a fundamental right to access?&lt;p&gt;Technology Suitability Check (Progressive Enhancement Focus):&lt;ul&gt;&lt;li&gt;Does the technologies core functionality allow the service to work without JS enabled?&lt;li&gt;Can the service still function reliably on low-powered or older devices when using this technology?&lt;li&gt;Is the final output from the technology easily accessible and usable with assistive technologies, regardless of the device used?&lt;li&gt;Does the technology degrade gracefully in poor network conditions, such as on a 3G connection or in rural areas?&lt;li&gt;Are all critical user journeys still functional when JS fails to load, or is blocked?&lt;/ul&gt;&lt;p&gt;If the answer to any of the questions above is &quot;no&quot;, then the technology probably isn&#39;t a great fit for a public service that needs to be maintained for years (or even decades!).&lt;p&gt;The last point in the list above is incredibly important:&lt;blockquote&gt;&lt;p&gt;Are all critical user journeys still functional when JS fails to load, or is blocked?&lt;/blockquote&gt;&lt;p&gt;I think this is where there was a lot of misunderstanding in terms of Progressive Enhancement in government. I continually strive to highlight that a JavaScript-only journey doesn&#39;t require a direct 1-to-1 correlation with its progressively enhanced foundation. As long as for each user journey a user can complete their task, quickly and easily, then the use of JavaScript is fine.&lt;p&gt;For example, consider a feature-rich JavaScript dashboard built to enter user data into a backend database. If a simple HTML form with a submit button can achieve the same outcome (which it often can), then the dashboard is acceptable only if the HTML form provides a reliable fallback for situations where JavaScript is unavailable, such as when it fails to load due to a limited data plan, a poor connection, or a low specification device.&lt;p&gt;During a service assessment, the key question is whether the dashboard meaningfully improves data entry and user interaction, or whether it exists purely for the sake of using new technology. Adopting new tools without clear justification is not acceptable for a government service. I consider such an approach to be driven by a desire to boost a developer&#39;s CV or LinkedIn profile. E.g. CDD (CV-Driven Development) or LDD (LinkedIn-Driven Development).&lt;h2 id=&quot;12-lessons-for-the-future&quot;&gt;12. Lessons for the Future&lt;/h2&gt;&lt;h3 id=&quot;what-these-legacy-practices-teach-us-today&quot;&gt;What these legacy practices teach us today&lt;/h3&gt;&lt;p&gt;If there is one takeaway from this post, it is that Frontend Development has &lt;strong&gt;never&lt;/strong&gt; stood still. What counts as best practice today can feel outdated or even “legacy” tomorrow. That constant state of reinvention is what first drew me in back in the late 90s, and it is what continues to excite me now. Backend development always seemed a little too steady, too predictable. Frontend, on the other hand, has always lived on the edge of change.&lt;p&gt;But what is different today is the scale of change ahead of us. With the rise of &lt;a href=&quot;https://livebench.ai/#/&quot;&gt;Artificial Intelligence (AI)&lt;/a&gt;, we may be standing on the edge of a shift as significant as the birth of the modern Web itself. Just as the early internet reshaped how we live and work, the combination of AI and the Web could redefine what it even means to build, design, and interact online. The coming years are not just going to be interesting, they could mark a turning point in the history of our craft.&lt;h3 id=&quot;applying-lessons-to-modern-frontend-work&quot;&gt;Applying lessons to modern frontend work&lt;/h3&gt;&lt;h4 id=&quot;core-principle&quot;&gt;Core principle&lt;/h4&gt;&lt;p&gt;Choose optimal solutions for the enduring parts of the stack: HTML, CSS, and JavaScript are the stable contract. Prioritise the most straightforward and maintainable approach to delivering clean, accessible HTML, efficient CSS, and lightweight, well scoped JavaScript. The further you move away from those three, the harder accessibility, maintenance, and performance become. Let the browser and the web platform do the heavy lifting.&lt;h4 id=&quot;practical-rules-to-work-by&quot;&gt;Practical rules to work by&lt;/h4&gt;&lt;h5 id=&quot;1-start-from-the-web-platform&quot;&gt;1. Start from the Web Platform&lt;/h5&gt;&lt;p&gt;Prefer native elements and platform features before adding libraries. Use form controls, semantic HTML, CSS layout, media queries, inert, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/details&quot;&gt;details&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/summary&quot;&gt;summary&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/dialog&quot;&gt;dialog&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch&quot;&gt;fetch&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API&quot;&gt;URLPattern&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver&quot;&gt;IntersectionObserver&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Web_components&quot;&gt;Web Components&lt;/a&gt; where they fit.&lt;h5 id=&quot;2-progressive-enhancement-as-a-default&quot;&gt;2. Progressive enhancement as a default&lt;/h5&gt;&lt;p&gt;Deliver meaningful HTML first, enhance with CSS, then layer JavaScript for interactivity. Critical journeys should still work when scripts fail or load slowly.&lt;h5 id=&quot;3-ship-less-code&quot;&gt;3. Ship less code&lt;/h5&gt;&lt;p&gt;Adopt a dependency diet. Each abstraction must earn its keep through measurable value. Small utilities over frameworks by default. If a framework is chosen, configure it to output lean HTML, CSS, and JS.&lt;h5 id=&quot;4-accessibility-first-not-last&quot;&gt;4. Accessibility first, not last&lt;/h5&gt;&lt;p&gt;Use semantic structure, proper labels, roles only when needed, visible focus, real buttons and links, reduced motion preferences, and test with keyboard and screen readers. Performance &lt;strong&gt;100% is&lt;/strong&gt; an accessibility feature.&lt;h5 id=&quot;5-performance-budgets-and-baselines&quot;&gt;5. Performance budgets and baselines&lt;/h5&gt;&lt;p&gt;Set budgets for bundle size, interaction latency, and memory. Track Core Web Vitals from real users. Fail builds that exceed budgets. Optimise for first input delay, input responsiveness, and low CPU use on mid-range devices.&lt;h5 id=&quot;6-keep-the-build-simple&quot;&gt;6. Keep the build simple&lt;/h5&gt;&lt;p&gt;Prefer standard tooling that converges to web standards. Use the minimum build steps required. Long pipelines increase failure modes and slow iteration.&lt;h5 id=&quot;7-design-for-resilience&quot;&gt;7. Design for resilience&lt;/h5&gt;&lt;p&gt;Favour server rendering for first paint, hydrate only what is interactive, cache well, and handle partial failure gracefully. Make error states explicit.&lt;h5 id=&quot;8-document-the-escape-hatches&quot;&gt;8. Document the escape hatches&lt;/h5&gt;&lt;p&gt;Where you choose abstractions, document how to reach the underlying HTML, CSS, and JS. Future teams should be able to debug without learning a bespoke stack.&lt;h5 id=&quot;9-measure-before-you-change&quot;&gt;9. Measure before you change&lt;/h5&gt;&lt;p&gt;Add observability. Use &lt;a href=&quot;https://en.wikipedia.org/wiki/Real_user_monitoring&quot;&gt;Real User Monitoring (RUM)&lt;/a&gt; to guide work. Optimise the slowest real user paths, not synthetic microbenchmarks.&lt;h5 id=&quot;10-plan-for-upgrades&quot;&gt;10. Plan for upgrades&lt;/h5&gt;&lt;p&gt;Last, but not least, prefer tools with clear deprecation policies and migration paths. Avoid lock in. Isolate framework code behind simple boundaries so you can replace parts without rewriting the product.&lt;h5 id=&quot;a-quick-decision-test&quot;&gt;A quick decision test&lt;/h5&gt;&lt;ol&gt;&lt;li&gt;Can this be done with native HTML or CSS alone?&lt;li&gt;If not, can a few lines of vanilla JS do it without a dependency?&lt;li&gt;If not, does a library reduce long term cost and keep output close to the platform?&lt;li&gt;If a framework is still justified, can it produce accessible HTML by default and degrade gracefully?&lt;/ol&gt;&lt;h5 id=&quot;closing-thought&quot;&gt;Closing thought&lt;/h5&gt;&lt;p&gt;Technologies come and go, but the contract with the browser remains. Choose the simplest path that produces high quality HTML, CSS, and JavaScript. The closer you stay to the platform, the easier your product will be to maintain, to make accessible, and to run fast at scale.&lt;h2 id=&quot;post-summary&quot;&gt;Post Summary&lt;/h2&gt;&lt;p&gt;I have to admit, my posts always seem to take on a life of their own and end up being longer than I plan. Concise writing might be a goal for another day. If you made it all the way here, congratulations, you’ve officially joined the “end of post club”! I really hope you found this journey as enjoyable to read as it was for me to write. Revisiting these ideas was a real trip down memory lane, and it reminded me of things I hadn’t thought about in years.&lt;p&gt;Your thoughts and feedback are always welcome. If I’ve overlooked a method or technique you think deserves a mention, let me know and I’ll happily credit you in the changelog. Thanks again for sticking with me to the very end, and if you’d like to &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;share your thoughts, you can do so here&lt;/a&gt;.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;26/08/25: Initial post published.&lt;li&gt;27/08/25: Fixed number ordering of headers.&lt;li&gt;27/08/25: Added Table of Contents for easier navigation!&lt;li&gt;28/08/25: Added Silverlight and Java Applets to the post (Thanks to an AI hallucination, regarding browser plugins from the past)&lt;li&gt;17/09/25: Thanks to Sven Kannengiesser for drawing my attention to the use of ‘here’ anchors. I have now revised them to be contextual and accessible.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Configuring your Content-Security-Policy on your development environment in 11ty</title>
    <link href="https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/" />
    <updated>2025-02-04T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/</id>
    <content type="html">&lt;p&gt;This is just a short post to discuss how I improved my &lt;code&gt;Content-Security-Policy&lt;/code&gt; (CSP), on my local development environment in 11ty. This is essentially a follow-on post from my &lt;a href=&quot;https://nooshu.com/blog/2024/12/28/securing-your-static-website-with-http-response-headers/&quot;&gt;Securing your static website with HTTP response headers&lt;/a&gt; I wrote last year.&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: this post isn’t exclusive to 11ty—it’s just the static site generator I use for this site. The code can be easily adapted for any static site running on a Node.js-based server—or practically any local web server!&lt;h2 id=&quot;why-is-this-useful&quot;&gt;Why is this useful?&lt;/h2&gt;&lt;p&gt;&lt;code&gt;&amp;lt;rant&gt;&lt;/code&gt;It&#39;s incredibly useful for me because my Cloudflare build currently takes 7 minutes and 30 seconds to complete! I&#39;ve no idea why it takes this long, and as of yet I&#39;ve had absolutely no response from Cloudflare on why this is! What I &lt;em&gt;do&lt;/em&gt; know is that something happened on the 19th December 2024 between 12:33AM and 12:19PM because my Cloudflare Pages build time increased by a massive 460%! See the screenshots below for proof of that fact:&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-300.webp 300w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-600.webp 600w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-814.webp 814w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-1200.webp 1200w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-1748.webp 1748w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Screenshot of my Cloudflare Pages build process taking 1 minute 30 seconds for a change I made with the git hash of efd9011&quot; decoding=&quot;async&quot; height=&quot;146&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-300.png 300w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-600.png 600w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-814.png 814w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-1200.png 1200w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/Inwi9dyaSd-1748.png 1748w&quot; width=&quot;1748&quot;&gt;&lt;/picture&gt;&lt;p&gt;Note the date, time, and git hash in the screenshot above: 12:33AM December 19, 2024, hash &lt;code&gt;efd9011&lt;/code&gt;. Now let&#39;s compare it to the screenshot below:&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;&lt;picture&gt;&lt;source sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; srcset=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-300.webp 300w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-600.webp 600w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-814.webp 814w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-1200.webp 1200w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-1736.webp 1736w&quot; type=&quot;image/webp&quot;&gt;&lt;img alt=&quot;Screenshot of my Cloudflare Pages build process taking 7 minute 22 seconds! For a change I made with the git hash of efd9011, a 460% increase!&quot; decoding=&quot;async&quot; height=&quot;168&quot; loading=&quot;lazy&quot; sizes=&quot;(max-width: 814px) calc(100vw - 53.554), 750px&quot; src=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-300.png&quot; srcset=&quot;https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-300.png 300w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-600.png 600w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-814.png 814w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-1200.png 1200w, https://nooshu.com/blog/2025/02/04/configuring-your-content-security-policy-on-your-development-environment-in-11ty/x7PPa9X04n-1736.png 1736w&quot; width=&quot;1736&quot;&gt;&lt;/picture&gt;&lt;p&gt;So in this screenshot above, we have the same git hash: &lt;code&gt;efd9011&lt;/code&gt;, but 12 hours have passed, it is now 12:19PM December 19, 2024.&lt;p&gt;Clearly my code hasn&#39;t changed because the git hash is the identical and nothing else has been committed, but for some reason my build time has increased by 460%! It&#39;s my guess that something changed on Cloudflare&#39;s side in this 12-hour period to drastically increase the build time. Even though I posted about it on the &lt;a href=&quot;https://discord.com/invite/cloudflaredev&quot;&gt;Cloudflare Discord server&lt;/a&gt; and the &lt;a href=&quot;https://community.cloudflare.com/t/11ty-v3-0-0-build-time-jumped-from-1min-20-to-7-minutes/750917&quot;&gt;Cloudflare Community Forums&lt;/a&gt;, I&#39;ve had no response to tell me what was changed and if it is possible to fix it. So if anyone from Cloudflare is reading, I&#39;m begging you, please, can you investigate what changed in that 12-hours and tell me if there&#39;s something I can change to drop my build time back to 1 minute 30 seconds!&lt;code&gt;&amp;lt;/rant&gt;&lt;/code&gt;&lt;p&gt;So you&#39;re probably asking: &quot;What on earth has any of this got to do with a &quot;Content-Security-Policy&quot; response header? Well, a 1-minute 30-second rebuild time to update my site&#39;s CSP in the 11ty &lt;code&gt;_headers&lt;/code&gt; file is a reasonable time to wait to test for issues. But waiting 7 minutes 20 seconds for every little change just isn&#39;t efficient or workable! Quite frankly it&#39;s incredibly frustrating. And if you&#39;ve ever worked on writing a CSP before you will realise how convoluted and unreasonably lengthy they can become! There had to be a better way to do this, and there is! So let me take you through the node.js / 11ty solution I used below to make my latest CSP changes.&lt;h2 id=&quot;the-code&quot;&gt;The code&lt;/h2&gt;&lt;p&gt;Let&#39;s have a look at the code:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This is my site&#39;s Content Security Policy.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Modify this CSP, don&#39;t just copy / paste it! It will break your site!&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// You can also use `var` and `let` depending on your coding syntax, they all work&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CSP&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
base-uri &#39;self&#39;;
child-src &#39;self&#39;;
connect-src &#39;none&#39;;
default-src &#39;none&#39;;
img-src &#39;self&#39; https://v1.indieweb-avatar.11ty.dev/;
font-src &#39;self&#39;;
form-action &#39;self&#39; https://webmention.io https://submit-form.com/DmOc8anHq;
frame-ancestors &#39;self&#39;;
frame-src &#39;self&#39; https://player.vimeo.com/ https://www.slideshare.net/ https://www.youtube.com/ https://giscus.app/ https://www.google.com/;manifest-src &#39;self&#39;;
media-src &#39;self&#39;;
object-src &#39;none&#39;;
script-src &#39;self&#39; https://ajax.cloudflare.com https://giscus.app/ https://www.google.com/ https://www.gstatic.com/;
style-src &#39;self&#39; https://giscus.app/;
worker-src &#39;self&#39;;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replaceAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;&#92;n&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This is the middleware for our 11ty development server&lt;/span&gt;
eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setServerOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;middleware&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.html&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;text/html; charset=UTF-8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setHeader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Content-Security-Policy&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CSP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you will see I have formatted the CSP using template literals to make the CSP more readable, and thus easier to modify and test on my local environment. You can find the &lt;a href=&quot;https://gist.github.com/Nooshu/472183d0586a52dd79e0c7d8140ddac6&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;public gist for the code above here&lt;/a&gt;.&lt;p&gt;A couple of notes on this code:&lt;ol&gt;&lt;li&gt;Notice how I removed newlines from the final CSP variable. Using &lt;code&gt;const&lt;/code&gt; signals to other developers that it shouldn&#39;t change, but since strings are immutable in JavaScript, modifying it using &lt;code&gt;.replace()&lt;/code&gt; creates a new valid CSP header string.&lt;li&gt;I&#39;ve added detection for both &quot;naked URLs&quot; and those ending in &lt;code&gt;/index.html&lt;/code&gt;. This is just for convenience really, as it saves having to add the &lt;code&gt;index.html&lt;/code&gt; to URLs before the response header is sent.&lt;li&gt;Lastly, the above CSP in its current form &lt;strong&gt;will&lt;/strong&gt; break 11ty&#39;s live reload functionality because the &lt;code&gt;connect-src&lt;/code&gt; in the CSP is set to &lt;code&gt;none&lt;/code&gt; which tells the browser to block all network requests initiated by the page.&lt;/ol&gt;&lt;p&gt;The following network communication technologies are blocked by this directive:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API&quot;&gt;Fetch API&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest&quot;&gt;XMLHttpRequest&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebSocket&quot;&gt;WebSockets&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/EventSource&quot;&gt;EventSource (Server-Sent Events)&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API&quot;&gt;WebRTC connections&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;In order to fix point 3 there are 2 options:&lt;h3 id=&quot;1-modify-the-connect-src-directive&quot;&gt;1) Modify the &lt;code&gt;connect-src&lt;/code&gt; directive&lt;/h3&gt;&lt;p&gt;This is the obvious fix for the issue:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// add the following to your CSP variable&lt;/span&gt;
connect&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; wss&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; ws&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By modifying the &lt;code&gt;connect-src&lt;/code&gt; directive, we are allowing connections on localhost via HTTP and WebSockets (which 11ty&#39;s live reload script relies on to push updates to the browser without having to reload the whole page). And we are also allowing WebSocket connections that are both encrypted (&lt;code&gt;wss:&lt;/code&gt;) and unencrypted (&lt;code&gt;ws:&lt;/code&gt;).&lt;h3 id=&quot;2-modify-the-default-src-directive&quot;&gt;2) Modify the &lt;code&gt;default-src&lt;/code&gt; directive&lt;/h3&gt;&lt;p&gt;This is a less obvious fix for the issue:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// add the following to your CSP variable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;src &lt;span class=&quot;token string&quot;&gt;&#39;self&#39;&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;ws&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; wss&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; ws&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;localhost&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The reason this works is that &lt;code&gt;default-src&lt;/code&gt; is the &quot;fallback&quot; directive. So assuming your &lt;code&gt;connect-src&lt;/code&gt; is set to &lt;code&gt;self&lt;/code&gt; then the following &lt;code&gt;ws: wss: http://localhost:* ws://localhost:*;&lt;/code&gt; would still be followed because the directive is falling back to &lt;code&gt;default-src&lt;/code&gt;.&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: don&#39;t use &lt;code&gt;default-src&lt;/code&gt; if your &lt;code&gt;connect-src&lt;/code&gt; is set to &lt;code&gt;none&lt;/code&gt;! The value of &lt;code&gt;none&lt;/code&gt; will still block all connections and override the &lt;code&gt;default-src&lt;/code&gt; settings.&lt;p&gt;The advantage of modifying the &lt;code&gt;default-src&lt;/code&gt; directive is that it will be applied to other directives like &lt;code&gt;script-src&lt;/code&gt; and &lt;code&gt;img-src&lt;/code&gt;. So it really depends on your CSP requirements for your local development environment! For most readers, I&#39;d guess modifying the &lt;code&gt;connect-src&lt;/code&gt; directive will be enough. But I&#39;ve added both options for completeness.&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;Believe it or not, I kept the blog post short this time! I hope this code snippet makes configuring your 11ty CSP a breeze! As always, thanks for reading and if you have any feedback or comments, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;!&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;04/02/25: Initial post published.&lt;li&gt;09/02/25: Thanks to vrugtehagel from discord for his couple of tweaks to the &lt;code&gt;const CSP&lt;/code&gt; code. The small changes can be seen in the &lt;a href=&quot;https://gist.github.com/Nooshu/472183d0586a52dd79e0c7d8140ddac6/revisions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gist revisions&lt;/a&gt; if you are interested.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Lets create a plaintext RSS feed with 11ty</title>
    <link href="https://nooshu.com/blog/2025/01/29/lets-create-a-plaintext-rss-feed-with-11ty/" />
    <updated>2025-01-29T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/01/29/lets-create-a-plaintext-rss-feed-with-11ty/</id>
    <content type="html">&lt;p&gt;Before writing this blog post I had 3 types of RSS feeds for my blog posts:&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/feed/feed-atom.xml&quot;&gt;Atom&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/feed/feed-rss.xml&quot;&gt;RSS&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/feed/feed.json&quot;&gt;JSON&lt;/a&gt;&lt;/ol&gt;&lt;p&gt;In this post, I&#39;m going to add a 4th type: Plaintext.&lt;h2 id=&quot;origin&quot;&gt;Origin&lt;/h2&gt;&lt;p&gt;A few months ago I was browsing my contacts and past colleagues looking for people to follow for my &lt;a href=&quot;https://nooshu.com/blogroll/&quot;&gt;blogroll page&lt;/a&gt;. In doing so I just so happened to remember &lt;a href=&quot;https://mastodon.social/@edent&quot;&gt;Terence Eden&lt;/a&gt; who I worked with at Government Digital Service (GDS). Terence (or &lt;a href=&quot;https://edent.tel/&quot;&gt;edent&lt;/a&gt;, as he is known on most social networks), is a prolific blogger! I honestly don&#39;t know where he gets the time to blog so much &lt;strong&gt;and&lt;/strong&gt; work, eat, sleep, blink, and breathe! While looking at the source code of his blog for his RSS feed, I just so happened to notice he had a &lt;a href=&quot;https://shkspr.mobi/blog/.txt&quot;&gt;plaintext version of his RSS feed&lt;/a&gt;. At the time it was responding with a 404, so I let him know on &lt;a href=&quot;https://mastodon.social/@edent&quot;&gt;Mastodon&lt;/a&gt;, and he fixed it in a matter of minutes. Once fixed, I could see how it was formatted, and I set it as a personal challenge to replicate this feature on this blog (once all other more important functionality had been added). Now you may be asking, why would you need a plaintext version? So, let&#39;s answer that question first!&lt;h2 id=&quot;why-add-a-plaintext-rss-feed&quot;&gt;Why add a plaintext RSS feed?&lt;/h2&gt;&lt;p&gt;Other than a personal challenge, what are the reasons for adding a plaintext version of your RSS feed?&lt;h3 id=&quot;greater-syndication&quot;&gt;Greater syndication&lt;/h3&gt;&lt;p&gt;Now I have no idea how people like to read my content (I&#39;m just very glad they do!). So if there&#39;s any way I can make their lives easier by adding multiple formats of the same RSS feed, then I&#39;m happy to do it. After all, I&#39;m always looking for ways to make this blog more open and inclusive. Even if it&#39;s only for a handful of readers, then it&#39;s worth adding the functionality, as it really is &quot;set it and forget it&quot; once deployed!&lt;h3 id=&quot;command-line-feed-readers&quot;&gt;Command-line feed readers&lt;/h3&gt;&lt;p&gt;I had no idea until I started researching the topic that there are users who use their terminal window as an RSS feed reader. For this they use programs like:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/newsboat/newsboat&quot;&gt;newsboat&lt;/a&gt;: &quot;An RSS/Atom feed reader for text terminals&quot; — It looks to have a very active repository on GitHub with 217 forks and 3,100 stars!&lt;li&gt;&lt;a href=&quot;https://lzone.de/liferea&quot;&gt;Liferea&lt;/a&gt;: &quot;Liferea (Linux Feed Reader), a newsreader for GTK/GNOME&quot; — Again an active &lt;a href=&quot;https://github.com/lwindolf/liferea&quot;&gt;GitHub repository&lt;/a&gt; with 129 forks and 830+ stars. I also noticed the project is over 20 years old (looking at some of the commits)!&lt;li&gt;&lt;a href=&quot;https://github.com/martinrotter/rssguard&quot;&gt;RSS Guard&lt;/a&gt;: &quot;Feed reader (podcast player and also Gemini protocol client) which supports RSS/ATOM/JSON and many web-based feed services.&quot; — another RSS client with an active GitHub repository with 130 forks and 1,800 stars. It&#39;s not surprising really considering the &lt;a href=&quot;https://rssguard.readthedocs.io/en/stable/supported-os.html&quot;&gt;number of operating systems it supports&lt;/a&gt;!&lt;/ul&gt;&lt;p&gt;So if you were in any doubt that plaintext RSS feed users don&#39;t exist, then I think the numbers above prove otherwise! (before you get angry, I do realise that not all these three programs users will be plaintext users!).&lt;h3 id=&quot;privacy-and-security-concerns&quot;&gt;Privacy and Security Concerns&lt;/h3&gt;&lt;p&gt;You only have to look at the Web Almanac 2024&#39;s chapters on &lt;a href=&quot;https://almanac.httparchive.org/en/2024/privacy&quot;&gt;Privacy&lt;/a&gt; and &lt;a href=&quot;https://almanac.httparchive.org/en/2024/security&quot;&gt;Security&lt;/a&gt; to realise how important these two topics are on the modern web. So it&#39;s completely understandable that RSS users are concerned about this when they subscribe to RSS feeds. Thankfully, this is where plaintext RSS feeds excel. By using plaintext RSS feeds, users can avoid loading external content such as images or scripts, thus reducing the risk of tracking and also enhancing their privacy.&lt;h3 id=&quot;accessibility&quot;&gt;Accessibility&lt;/h3&gt;&lt;p&gt;I&#39;ve seen accessibility brought up as a plus for plaintext RSS feeds, but honestly, I’m a little sceptical. Sure, a screen reader can read the content easily enough, but when it comes to navigation and overall user experience for someone with accessibility needs, navigating a plaintext feed seems like it’d be a step backwards. Wouldn’t something like HTML or XML, with its semantic markup and built-in navigation, be a way better option? That said, I’m totally open to being wrong here—if I’ve got this all incorrect, feel free to &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;.&lt;h3 id=&quot;data-efficiency&quot;&gt;Data Efficiency&lt;/h3&gt;&lt;p&gt;This one really doesn&#39;t need much discussion, really. You can&#39;t get much more minimal in terms of bandwidth usage than a plaintext RSS feed. No images, no JavaScript, no markup, just pure content. It certainly makes for an excellent source of content for users with limited internet access or those operating in low-bandwidth environments.&lt;h3 id=&quot;customisation-and-integration&quot;&gt;Customisation and Integration&lt;/h3&gt;&lt;p&gt;Tools like &lt;a href=&quot;https://github.com/Kombustor/rss-fulltext-proxy&quot;&gt;RSS Fulltext Proxy&lt;/a&gt; and &lt;a href=&quot;http://ftr.fivefilters.org/&quot;&gt;Five Filters&lt;/a&gt; allow for text extraction from any website to convert partial feeds into full-text. This allows for integration into any feed reader, without plugins, or additional configuration. These are exceptional options if you want to integrate a particular feed into one of your workflows.&lt;h3 id=&quot;reliability-and-readability&quot;&gt;Reliability and Readability&lt;/h3&gt;&lt;p&gt;You literally can&#39;t get a more robust format than plaintext. It is universally supported and small. And in terms of the resulting readability, there&#39;s no cookie banners, adverts, or newsletter signup forms that pop up halfway through an article. Thankfully, none of these annoyances are possible in plaintext, so you can consume all the content without interruption. It reminds me of how the World Wide Web used to be before it was commercialised beyond recognition! Take a read of the &lt;a href=&quot;https://info.cern.ch/hypertext/WWW/TheProject.html&quot;&gt;world&#39;s first website&lt;/a&gt;, and you get an idea of what &lt;a href=&quot;https://www.home.cern/science/computing/birth-web&quot;&gt;Sir Tim Berners-Lee&lt;/a&gt; had in mind when he invented it in 1989 at CERN.&lt;blockquote&gt;&lt;p&gt;The web was originally conceived and developed to meet the demand for automated information-sharing between scientists in universities and institutes around the world.&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;The WorldWideWeb (W3) is a wide-area hypermedia information retrieval initiative aiming to give universal access to a large universe of documents.&lt;/blockquote&gt;&lt;p&gt;Anyway, enough of the &quot;why&#39;s&quot; let&#39;s crack on with the 11ty implementation!&lt;h2 id=&quot;the-code&quot;&gt;The code&lt;/h2&gt;&lt;h3 id=&quot;eleventy-config-js&quot;&gt;eleventy.config.js&lt;/h3&gt;&lt;p&gt;First, let&#39;s modify the 11ty config file:&lt;p&gt;Here we are telling 11ty to process &lt;code&gt;.txt&lt;/code&gt; files as template files.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTemplateFormats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next we need to configure 11ty and tell it how to handle &lt;code&gt;.txt&lt;/code&gt; files with a custom handler.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addExtension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property literal-property&quot;&gt;outputFileExtension&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;txt&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token function function-variable&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;inputContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; inputContent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The code is fairly self-explanatory &lt;code&gt;outputFileExtension&lt;/code&gt; is obvious, the resulting output will have a &lt;code&gt;.txt&lt;/code&gt; extension. The &lt;code&gt;compile&lt;/code&gt; function is a simple pass-through. It takes the &lt;code&gt;txt&lt;/code&gt; input and returns the original content unchanged.&lt;h3 id=&quot;feed-txt-njk&quot;&gt;feed.txt.njk&lt;/h3&gt;&lt;p&gt;Next up is the template for the plaintext feed output. I&#39;m using Nunjucks as that&#39;s the main templating language I use across this blog, but I&#39;m sure you&#39;d be able to any other templating language 11ty supports too. This file sits in my &lt;code&gt;/content/feed/&lt;/code&gt; directory, so 11ty will process it as a &lt;code&gt;njk&lt;/code&gt; file in the output.&lt;pre class=&quot;language-njk&quot; tabindex=&quot;0&quot; data-gist=&quot;da04fd0322e45936fbbc5c663ebc9677&quot;&gt;&lt;code class=&quot;language-njk&quot;&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;feed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;txt&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;eleventyComputed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;layout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;
# &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
## &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;fulldescription&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;collections&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;posts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;reverse&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;index0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;Published&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;readableDate&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;Main&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;Content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;templateContent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;striptags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;decodeHtmlEntities&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;loop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;last&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;End&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;safe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;



&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;endfor&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A Gist for this template &lt;a href=&quot;https://gist.github.com/Nooshu/da04fd0322e45936fbbc5c663ebc9677&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;is here&lt;/a&gt;, if you find that easier to read.&lt;p&gt;There&#39;s a fair amount going on in this template file, but it is essentially:&lt;ol&gt;&lt;li&gt;Setting where I want the feed to sit in my final site output (&lt;code&gt;permalink&lt;/code&gt;)&lt;li&gt;Overriding the default layout dynamically during the build process (&lt;code&gt;layout: null&lt;/code&gt;)&lt;li&gt;Populating the top of the feed with my basic blog metadata.&lt;li&gt;Looping through all by blog posts in reverse (newest first).&lt;li&gt;Limiting the number of posts in the feed to 10.&lt;li&gt;Outputting the parts of each post I want in the feed using Nunjucks and cleaning up the output using various filters, which I will go through next.&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Code Notes&lt;/strong&gt;:&lt;ol&gt;&lt;li&gt;You may be looking at this template and wondering what is going on with all the space between the logic. Well, since this template is outputting plaintext, this is all intentional spacing to make the final output more readable.&lt;li&gt;I&#39;m using &lt;code&gt;loop.index0&lt;/code&gt; which is the current iteration of the loop but 0 indexed because it was the only way that seemed to work for me to limit the number of posts. I think I must have a weird &lt;code&gt;collections.posts&lt;/code&gt; setup somewhere because regardless of what I tried, nothing worked. Likewise, I believe the native &lt;code&gt;slice(0, 10)&lt;/code&gt; &lt;em&gt;should&lt;/em&gt; do the same thing, but for me, the loop stopped working altogether. I tried debugging it for a couple of hours and eventually stuck with the &lt;code&gt;loop.index0&lt;/code&gt; setup because it actually worked. However, if anyone has any ideas on why the native &lt;code&gt;slice()&lt;/code&gt; function on a &lt;code&gt;collections.posts&lt;/code&gt; doesn&#39;t work, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;! I&#39;d love to get to the bottom of what the issue is!&lt;li&gt;You may notice my use of the &lt;code&gt;-%}&lt;/code&gt; in the template, this is me telling Nunjucks to trim any whitespace (spaces, tabs, newlines) directly following the tag. This was required because it was adding numerous new lines in the resulting output. It&#39;s when I discover little intricacies in Nunjucks like this, that I realise what a versatile templating language it actually is!&lt;/ol&gt;&lt;h3 id=&quot;filters&quot;&gt;filters&lt;/h3&gt;&lt;p&gt;Most of the filters used in the above template are all pretty standard to the Nunjucks language:&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#safe&quot;&gt;safe&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#reverse&quot;&gt;reverse&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#striptags-value-preserve_linebreaks&quot;&gt;striptags(true)&lt;/a&gt;&lt;/ul&gt;&lt;p&gt;I only added one custom 11ty filter for this functionality.&lt;h4 id=&quot;decodehtmlentities&quot;&gt;decodeHtmlEntities&lt;/h4&gt;&lt;p&gt;For some reason, I had some very odd character encoding issues, and the only way I could resolve the issue was to create a custom 11ty filter to search and replace them throughout the content. I looked through the &lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#filters&quot;&gt;default Nunjucks filters&lt;/a&gt; for a solution, but filters like &lt;code&gt;escape&lt;/code&gt; and &lt;code&gt;forceescape&lt;/code&gt; didn&#39;t work. I could have probably used Nunjucks native &lt;a href=&quot;https://mozilla.github.io/nunjucks/templating.html#replace&quot;&gt;replace&lt;/a&gt; filter, but that would have cluttered up the template, so decided to move it to an 11ty filter instead:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// clean up the HTML entities in the RSS feed text&lt;/span&gt;
eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addFilter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;decodeHtmlEntities&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&amp;([^;]+);&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;match&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; entity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; entities &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;amp&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;apos&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&#39;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;lt&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;gt&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;quot&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token property string-property&quot;&gt;&#39;nbsp&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39; &#39;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// Handle numeric entities&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entity&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;#&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; code &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; entity&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;x&#39;&lt;/span&gt;
							&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
							&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;entity&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromCharCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; entities&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;entity&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; match&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&#39;s basically just a fancy way to search and replace for multiple encoding issues it finds in the plaintext output.&lt;p&gt;Just to be clear, I wrote a really clunky version that did the same thing as this first. Basically, a chain of &lt;code&gt;text.replaceAll(&#39;&amp;ampamp;&#39;, &#39;&amp;&#39;).replaceAll(&#39;&amp;ampapos;&#39;, &quot;&#39;&quot;)...&lt;/code&gt; and so on, I think you get the idea.&lt;p&gt;It worked fine, but then I asked ChatGPT to optimise it, and this is the result it came up with! It&#39;s certainly not as readable, but it still does the job, and it also handles numeric entities too! Off the back of this example, I think AI used correctly can be a fantastic teaching tool, especially when it comes to random little filter functions like this.&lt;h3 id=&quot;adding-the-feed-to-your-head&quot;&gt;Adding the feed to your &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt;&lt;/h3&gt;&lt;p&gt;Once you have the feed up and running, it&#39;s time to make sure you have it visible to your users by adding it to your pages &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt;. It&#39;s dead simple, just like your other feeds, only the &lt;code&gt;type&lt;/code&gt; is set to &lt;code&gt;text/plain&lt;/code&gt;.&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;alternate&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/feed/feed.txt&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text/plain&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Nooshu - Matt Hobbs blog post Plaintext Feed.&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I also have links to all my feed formats in my footer so they are easy to find. So, finally, why not look at the result of the above code now by looking at my &lt;a href=&quot;https://nooshu.com/feed/feed.txt&quot;&gt;plaintext RSS feed&lt;/a&gt;.&lt;h2 id=&quot;sample-output&quot;&gt;Sample output&lt;/h2&gt;&lt;p&gt;Here&#39;s an example of the output from the code above:&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;# Nooshu - Matt Hobbs - Frontend web developer, turned engineering manager.
## This is the website of Matt Hobbs, who is a Frontend Engineering Manager from Oxfordshire, UK.
URL: https://nooshu.com

--- Start: The Speed Trifecta: 11ty, Brotli 11, and CSS Fingerprinting
Published on: 23 January 2025
https://nooshu.com/blog/2025/01/23/the-speed-trifecta-11ty-brotli-11-and-css-fingerprinting/

Main Content:
So recently, I have written two 11ty related blog posts:

Using an 11ty Shortcode to craft a custom CSS pipeline
Cranking Brotli up to 11 with Cloudflare Pro and 11ty

...more blogpost content here...

Post changelog:

23/01/25: Initial post published.

--- End: The Speed Trifecta: 11ty, Brotli 11, and CSS Fingerprinting&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Whew!&lt;/strong&gt; Another blog post in the books, and another shiny new feature added—this time, a working plaintext RSS feed! (That’s one more thing off the to-do list—always a win!) Thanks for stopping by! I hope you found this post interesting, maybe even &lt;em&gt;useful&lt;/em&gt;? As always, if you’ve got thoughts, or feedback, please &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;!&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;29/01/25: Initial post published.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>The Speed Trifecta: 11ty, Brotli 11, and CSS Fingerprinting</title>
    <link href="https://nooshu.com/blog/2025/01/23/the-speed-trifecta-11ty-brotli-11-and-css-fingerprinting/" />
    <updated>2025-01-23T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/01/23/the-speed-trifecta-11ty-brotli-11-and-css-fingerprinting/</id>
    <content type="html">&lt;p&gt;So recently, I have written two 11ty related blog posts:&lt;ol&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/&quot;&gt;Using an 11ty Shortcode to craft a custom CSS pipeline&lt;/a&gt;&lt;li&gt;&lt;a href=&quot;https://nooshu.com/blog/2025/01/05/cranking-brotli-up-to-11-with-cloudflare-pro-and-11ty/&quot;&gt;Cranking Brotli up to 11 with Cloudflare Pro and 11ty&lt;/a&gt;&lt;/ol&gt;&lt;p&gt;If you haven&#39;t read them, no problem, they are always there if you&#39;re ever struggling to sleep!&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;:&lt;p&gt;In the first post I look at how I&#39;ve automated CSS fingerprinting on production (Cloudflare), resulting in the ability to use long-life &lt;code&gt;Cache-Control&lt;/code&gt; directives like &lt;code&gt;max-age=31536000&lt;/code&gt; and the &lt;code&gt;immutable&lt;/code&gt; browser hint.&lt;p&gt;In the second post, I look at how you can improve Brotli compression by manually compressing your assets to Brotli setting 11 (max) then serving them via a Cloudflare Pro plan. This gives a significant improvement in file size over the Brotli compression setting of 4 that &lt;a href=&quot;https://blog.cloudflare.com/this-is-brotli-from-origin/&quot;&gt;Cloudflare uses for dynamic (on the fly) compression&lt;/a&gt;.&lt;p&gt;While working on both posts, it dawned on me that blending the approaches they cover would result in the ideal CSS configuration for my little blog. In this post, I&#39;ll show you exactly how I&#39;ve done it. Let&#39;s get started!&lt;h2 id=&quot;original-code&quot;&gt;Original code&lt;/h2&gt;&lt;p&gt;So I&#39;m going to be using the code I wrote in the CSS fingerprinting blog post I wrote recently. You can see it below, or in &lt;a href=&quot;https://gist.github.com/Nooshu/edfc2e382bc249e92ab238779357c93e&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this Gist&lt;/a&gt;.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;edfc2e382bc249e92ab238779357c93e&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; CleanCSS &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;clean-css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; crypto &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dotenv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// create a single instance of the CleanCSS function&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// to be used in file loops. Add additional optimisation settings in here.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CleanCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeDuplicateRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// turns on removing duplicate rules&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;manipulateCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;customCSS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cssPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// output the file with no fingerprinting if on the dev environment&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// (allows auto-reload when the CSS is modified)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEVENTY_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;development&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Using path.join for better cross-platform compatibility&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./.cache&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Check if input file exists first&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Input CSS file not found: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Ensure both cache and output directories exist&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dir &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Read the input CSS file&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Initialises a new hashing instance&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sha256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Feed CSS data into the hash function&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Specify the hash should be returned as a hexadecimal string&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Only take the first 10 characters of the hash&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// Generate our CSS Cache name&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;[&#92;/&#92;&#92;]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// This is where the file will be written&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cacheKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// store our manipulated CSS in this variable&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// check we have a cache directory&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// read the cached CSS file&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Use the memoized cleanCSS instance to minify the input CSS&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cleanCSS&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Split the input file path into its components (directory, filename, extension)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parsedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Use path.join for output paths&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ext&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Write the optimised CSS to the final output location with the hash in the filename&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// path manipulation for final URL&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hashedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; finalFilename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// return our final link element with optimised and fingerprinted CSS.&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hashedPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error processing CSS:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;install-zlib&quot;&gt;Install zlib&lt;/h3&gt;&lt;p&gt;The first thing we shall do is install the &lt;a href=&quot;https://www.npmjs.com/package/zlib&quot;&gt;zlib&lt;/a&gt; package from npm, as this is what we are going to use to compress the CSS file all the way up to 11 on production.&lt;pre class=&quot;language-sh&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; zlib&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Next, we import it into our ESM code above:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; zlib &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;zlib&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Don&#39;t worry, I&#39;m not going to go this slow all the way through the code. Feel free to &lt;a href=&quot;https://nooshu.com/blog/2025/01/23/the-speed-trifecta-11ty-brotli-11-and-css-fingerprinting/#final-code&quot;&gt;skip straight to the final code&lt;/a&gt; if you so wish!&lt;h3 id=&quot;setting-the-default-compression-level&quot;&gt;Setting the default compression level&lt;/h3&gt;&lt;p&gt;This is a completely optional step, it really depends on how, and where you store all your build variables. But I&#39;m going to be storing the Brotli Compression level I want to use on production both in the JS (as a default to fall back on if the environment variable doesn&#39;t exist), and also an environment variable on production. In the JavaScript below, set at 6 to show it can be any value between 0-11 (3 or 4 is what most CDN use for dynamic compression). Cloudflare&#39;s compression is &lt;a href=&quot;https://blog.cloudflare.com/results-experimenting-brotli/&quot;&gt;set to 4&lt;/a&gt;:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Default Brotli compression level if not set in the environment&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And also in my &lt;code&gt;.env&lt;/code&gt; file in development:&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;BROTLI_COMPRESSION_LEVEL=11&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now we set our compression level as a constant in our JavaScript for use later in the code:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Since our environment variable is currently set, compression will be set to 11, but if that variable isn&#39;t set, it will fall back to 6, the default value set above.&lt;h3 id=&quot;cloudflare-and-brotli&quot;&gt;Cloudflare and Brotli&lt;/h3&gt;&lt;p&gt;Before we go through the Brotli compression code, it&#39;s a good time to mention that Cloudflare does something automatically that is very useful for compressed files. Cloudflare Pages will automatically serve Brotli-compressed files (&lt;code&gt;.br&lt;/code&gt;) if available, with no additional configuration required on the Cloudflare side. See the &lt;a href=&quot;https://developers.cloudflare.com/speed/optimization/content/compression/&quot;&gt;Content Compression Documentation&lt;/a&gt; on Cloudflare for more information.&lt;p&gt;This means we don&#39;t need to rename the compressed CSS file from &lt;code&gt;index-hash.css.br&lt;/code&gt; back to &lt;code&gt;index-hash.css&lt;/code&gt; for a user&#39;s browser to recognise it as a CSS file. Handy huh! Don&#39;t worry if you don&#39;t use Cloudflare Pages, I also have a version written that renames the CSS file back to &lt;code&gt;index-hash-compressed.css&lt;/code&gt; for non-Cloudflare Pages, readers.&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: While testing the documented method above, I could not find a way to confirm that my static compressed version was being served &lt;strong&gt;automatically&lt;/strong&gt;. So in the code below I have removed this assumption and explicitly added my statically compressed version to the &lt;code&gt;&amp;lt;link&gt;&lt;/code&gt; tag. Please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt; if I&#39;m interpreting the documentation for this functionality wrong! As I&#39;d love to see it in action, and also how you verify the &lt;code&gt;.br&lt;/code&gt; file is actually being served automatically?&lt;h3 id=&quot;brotli-compression-code&quot;&gt;Brotli Compression code&lt;/h3&gt;&lt;p&gt;Now that I&#39;ve covered that bit of (potential) Cloudflare Pages automation, let&#39;s have a look at the actual compression code:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Brotli compression&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The output filename for the compressed file&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.br&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Only compress to Brotli if the file doesn&#39;t exist&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Set our zlib options here e.g. compression&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token comment&quot;&gt;// Use the level specified in the environment&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// zlib does it&#39;s compression magic!&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; zlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;brotliCompressSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// Write the compressed code to the output filename defined above&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;generate-the-css-link-tag&quot;&gt;Generate the CSS &lt;code&gt;&amp;lt;link&gt;&lt;/code&gt; tag&lt;/h3&gt;&lt;p&gt;The last thing to do now is mostly the same as what I had in the original code set out above:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// path manipulation for final URL&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hashedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// return our final link element with optimised and fingerprinted CSS.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hashedPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Only this time I&#39;m modifying the path to the Brotli compressed filename rather than the original uncompressed CSS file! Simple!&lt;p&gt;&lt;strong&gt;On Development&lt;/strong&gt;: Once added to the 11ty config as per my &lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/#usage&quot;&gt;previous post&lt;/a&gt;, my development CSS looks like this:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;// uncompressed CSS with no fingerprinting, auto-reload still functioning
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;On Production&lt;/strong&gt;: The CSS looks like this:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;// Brotli 11 compressed, minified and fingerprinted CSS
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index-b9fcfe85ef.css.br&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the version I have running on the site at the moment, why not view the page source and take a look.&lt;h3 id=&quot;finishing-touches&quot;&gt;Finishing touches&lt;/h3&gt;&lt;p&gt;That&#39;s the bulk of the work done, but if you were to run this code on production at present you&#39;d get a page full of naked HTML and no styling. This is because the Cloudflare Pages &lt;code&gt;_headers&lt;/code&gt; file needs to be modified, as Cloudflare Pages is now serving my Brotli 11 compressed CSS file, not the uncompressed version we had before.&lt;p&gt;Simply add the following to your &lt;code&gt;_headers&lt;/code&gt; file:&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;/css/*
Content-Encoding: br
Vary: Accept-Encoding
Content-Type: text/css&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&#39;s important to remember that this headers file is looking at the &lt;strong&gt;path&lt;/strong&gt; from which the file is served on the production website! It isn&#39;t (and can&#39;t) use an extension wildcard, as Cloudflare pages doesn&#39;t support it. It took me a while to figure this out I must admit!&lt;p&gt;So for example, The following &lt;strong&gt;doesn&#39;t work&lt;/strong&gt;:&lt;pre class=&quot;language-text&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;*.css
Content-Encoding: br
Vary: Accept-Encoding
Content-Type: text/css&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When I first migrated to Cloudflare Pages, I tried it and couldn&#39;t work out why it wasn&#39;t working. &lt;code&gt;/css/*&lt;/code&gt; is basically saying: &quot;Any file that resides in the &lt;code&gt;css&lt;/code&gt; directory on production will be served with the following headers&quot;.&lt;p&gt;Lastly, remember to add the following to your Cloudflare Pages environment variables. This can be plaintext if you like, since the variable is only storing a single integer (not a secret API key):&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;BROTLI_COMPRESSION_LEVEL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Once added, you should be good to go!&lt;h2 id=&quot;final-code&quot;&gt;Final code&lt;/h2&gt;&lt;p&gt;As promised, below is the final code that I just described above:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot; data-gist=&quot;218b9855c099f368f99560d2fd02c61e&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; zlib &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;zlib&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; CleanCSS &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;clean-css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; crypto &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dotenv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// An example of how you could add additional CleanCSS settings if required&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CleanCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeDuplicateRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Default Brotli compression level if not set in the environment&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;manipulateCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;customCSS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cssPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEVENTY_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;development&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./.cache&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Get compression level from the environment or use the default&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Input CSS file not found: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dir &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sha256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;[&#92;/&#92;&#92;]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cacheKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cleanCSS&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parsedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ext&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// Brotli compression&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// The output filename for the compressed file&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.br&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// Only compress to Brotli if the file doesn&#39;t exist&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Set our zlib options here e.g. compression&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token comment&quot;&gt;// Use the level specified in the environment&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// zlib does it&#39;s compression magic!&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; zlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;brotliCompressSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Write the compressed code to the output filename defined above&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hashedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hashedPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error processing CSS:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And here&#39;s a &lt;a href=&quot;https://gist.github.com/Nooshu/218b9855c099f368f99560d2fd02c61e&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Gist for the code&lt;/a&gt; as well!&lt;h2 id=&quot;with-file-renaming&quot;&gt;With file renaming&lt;/h2&gt;&lt;p&gt;Now I understand not everyone will want to serve their CSS files using the &lt;code&gt;.br&lt;/code&gt; extension. So there&#39;s a version below that also renames the file for you back to &lt;code&gt;.css&lt;/code&gt;, and adds the &lt;code&gt;-compressed&lt;/code&gt; suffix to the filename as well. The rest of the code is identical to the version above:&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; zlib &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;zlib&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; dotenv &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dotenv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; CleanCSS &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;clean-css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;fs&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; crypto &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;crypto&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;path&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
dotenv&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// An example of how you could add additional CleanCSS settings if required&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CleanCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeDuplicateRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Default Brotli compression level if not set in the environment&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;manipulateCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;customCSS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cssPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ELEVENTY_ENV&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;development&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./.cache&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// Get compression level from the environment or use the default&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Input CSS file not found: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dir &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;sha256&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hex&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;[&#92;/&#92;&#92;]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;-&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cacheKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;utf8&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cleanCSS&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parsedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ext&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

			&lt;span class=&quot;token comment&quot;&gt;// Brotli compression with renaming&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compressedFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-compressed&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ext&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Only compress to Brotli if the file doesn&#39;t exist&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compressedFilename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Set our zlib options here e.g. compression&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; brotliCompressionLevel
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// zlib does it&#39;s compression magic!&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; zlib&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;brotliCompressSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;// Write the compressed code to the output filename defined above&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compressedFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hashedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; compressedFilename&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;./_site&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hashedPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error processing CSS:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The Gist for the code above &lt;a href=&quot;https://gist.github.com/Nooshu/e8a4b27496cd077f3ab7836e57705142&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;is here&lt;/a&gt;.&lt;p&gt;The above code will do the following:&lt;p&gt;&lt;strong&gt;On Development&lt;/strong&gt;: It will simply output:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Exactly as it did in the previous version.&lt;p&gt;&lt;strong&gt;On Production&lt;/strong&gt;: This is the output that will be seen:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index-b9fcfe85ef-compressed.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this instance, the CSS file has been Brotli compressed to 11, but it has also been renamed with the &lt;code&gt;-compressed&lt;/code&gt; suffix and the &lt;code&gt;.br&lt;/code&gt; extension has been removed. Technically, you don&#39;t need the suffix, but I&#39;ve added it to emphasise (and remind myself) that it isn&#39;t a &quot;standard&quot; CSS file in plain text.&lt;p&gt;And as I mentioned in the &lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/&quot;&gt;previous CSS Shortcode blog post&lt;/a&gt;. You&#39;ll now be able to use long cache life headers like:&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;/[css]/*
Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Safe in the knowledge that updating your CSS will generate a brand-new filename, effectively nullifying the existing cache. Check out the &lt;a href=&quot;https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/#headers-file&quot;&gt;previous post&lt;/a&gt; if you want more details on the above &lt;code&gt;_headers&lt;/code&gt; file code for Cloudflare.&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;Fantastic! You made it to the end of yet another 11ty blog post of mine, congrats! I&#39;m not sure if there&#39;s anything else I can do to optimise my CSS delivery to users at the moment?&lt;p&gt;Maybe preloading or the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API&quot;&gt;Speculation Rules API&lt;/a&gt;? Which Cloudflare already supports, and is currently enabled on my site. This functionality is called &quot;Speed Brain&quot; in the Cloudflare dashboard (under the &quot;Speed&quot; then &quot;Content Optimization&quot;). It&#39;s worth noting that this functionality is currently in Beta. But I don&#39;t think I need to do anything manually to use it, since Cloudflare rolled it out to all plans (including the free plan!), &lt;a href=&quot;https://community.cloudflare.com/t/speculation-rules-api-rollout-to-free-plans/708111&quot;&gt;back in September 2024&lt;/a&gt;.&lt;p&gt;But perhaps there are other optimisations I can make? Unknown, unknowns etc… As always, thanks for reading, I hope you found the post useful and if you have any feedback or comments &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;!&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;23/01/25: Initial post published.&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Using an 11ty Shortcode to craft a custom CSS pipeline</title>
    <link href="https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/" />
    <updated>2025-01-12T00:00:00Z</updated>
    <id>https://nooshu.com/blog/2025/01/12/using-an-11ty-shortcode-to-craft-a-custom-css-pipeline/</id>
    <content type="html">&lt;p&gt;I know I&#39;m still a bit of a 11ty n00b, so I hope this isn&#39;t frowned upon in the community, but on rebuilding my blog using 11ty, I decided not to use the standard &lt;a href=&quot;https://www.11ty.dev/docs/plugins/bundle/&quot;&gt;Bundle plugin&lt;/a&gt; that was added in &lt;a href=&quot;https://www.11ty.dev/blog/eleventy-v3/&quot;&gt;v3.0.0&lt;/a&gt;. Instead, I decided to write a custom &lt;a href=&quot;https://www.11ty.dev/docs/shortcodes/&quot;&gt;Shortcode&lt;/a&gt; to customise my CSS output. In this blog post, I will go through the code I have written in the hope it will help others and more importantly gather feedback from the community, to see if any improvements can be made to the code. Please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt; if you have any feedback.&lt;h2 id=&quot;my-requirements&quot;&gt;My Requirements&lt;/h2&gt;&lt;p&gt;First, what am I hoping to achieve with this Shortcode solution?&lt;ul&gt;&lt;li&gt;Maintain the auto-reload functionality that comes with 11ty when you modify your CSS locally (this is a super helpful feature I&#39;ve been a big fan of since &lt;a href=&quot;https://web.archive.org/web/20220317001804/https://livereload.com/&quot;&gt;LiveReload&lt;/a&gt; was released back in February 2011, and later &lt;a href=&quot;https://browsersync.io/&quot;&gt;BrowserSync&lt;/a&gt;).&lt;li&gt;Filename &lt;a href=&quot;https://en.wikipedia.org/wiki/Fingerprint_(computing)#Virtual_uniqueness&quot;&gt;fingerprinting&lt;/a&gt; to allow me to use long-life &lt;code&gt;Cache-Control&lt;/code&gt; response headers without having to worry about cache becoming outdated. (e.g. &lt;code&gt;max-age=31536000, immutable&lt;/code&gt;)&lt;li&gt;The ability to optimise my CSS output using the excellent &lt;a href=&quot;https://www.npmjs.com/package/clean-css&quot;&gt;clean-css&lt;/a&gt; Node plugin. It does more than just minify! Check out &lt;a href=&quot;https://www.npmjs.com/package/clean-css#optimization-levels&quot;&gt;all the optimisations&lt;/a&gt; it can help you if you haven&#39;t seen them.&lt;li&gt;Minimal impact on the 11ty build process by leveraging build caching in some way.&lt;li&gt;Set it and forget it, once it&#39;s added It requires minimal (or no ongoing maintenance), apart from updating dependencies, every so often!&lt;/ul&gt;&lt;h2 id=&quot;the-code&quot;&gt;The Code&lt;/h2&gt;&lt;p&gt;I&#39;ll quit the waffling and just show you the code I currently have building my CSS for this site. This code sits in its own file called &lt;code&gt;css-manipulation.js&lt;/code&gt; in a &lt;code&gt;_helpers&lt;/code&gt; directory in my 11ty root (i.e. the same level as &lt;code&gt;_data&lt;/code&gt;).&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * CSS Manipulation Module for Eleventy
 *
 * This module provides CSS processing, minification, and compression functionality
 * for an Eleventy static site generator. It handles:
 * - CSS minification using CleanCSS
 * - Content-based file hashing for cache busting
 * - Brotli compression for optimized delivery
 * - Build-time caching to avoid redundant processing
 * - Concurrent request handling to prevent duplicate work
 */&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Node.js built-in modules for file system operations, path manipulation, and hashing&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; crypto &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;crypto&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Used to create SHA-256 hashes of CSS content for cache busting&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File system operations (reading/writing files, checking existence)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; path &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Cross-platform path manipulation and joining&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Node.js zlib module for Brotli compression&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Brotli is a modern compression algorithm that provides better compression ratios than gzip&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; brotliCompressSync &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zlib&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// CleanCSS library for CSS minification and optimization&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This performs a wide range of optimizations to reduce file size&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; CleanCSS &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;clean-css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Environment configuration to check if we&#39;re in local development mode&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; env &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../_data/env.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * CleanCSS Configuration
 *
 * CleanCSS performs multi-level CSS optimization. This configuration enables
 * comprehensive minification while maintaining CSS functionality.
 *
 * Level 1 optimizations (basic cleanups):
 * - cleanupCharsets: Remove unnecessary @charset declarations
 * - normalizeUrls: Normalize and optimize URL() references
 * - optimizeBackground: Optimize background properties
 * - optimizeBorderRadius: Shorten border-radius values
 * - optimizeFilter: Optimize filter() functions
 * - optimizeFont: Optimize font-family declarations
 * - optimizeFontWeight: Convert font-weight to numeric values
 * - optimizeOutline: Optimize outline properties
 * - removeEmpty: Remove empty CSS rules
 * - removeNegativePaddings: Remove invalid negative padding values
 * - removeQuotes: Remove unnecessary quotes from URLs and identifiers
 * - removeWhitespace: Remove unnecessary whitespace
 * - replaceMultipleZeros: Shorten multiple zero values
 * - replaceTimeUnits: Optimize time unit values (e.g., 0s → 0)
 * - replaceZeroUnits: Remove units from zero values (e.g., 0px → 0)
 * - roundingPrecision: Round numeric values to 2 decimal places
 * - selectorsSortingMethod: Sort selectors in a standard order
 * - specialComments: Remove all comments (set to &quot;none&quot;)
 * - tidyAtRules: Clean up @-rules
 * - tidyBlockScopes: Clean up block scopes
 * - tidySelectors: Clean up and optimize selectors
 * - transform: Custom transformation function (empty, no custom transforms)
 *
 * Level 2 optimizations (advanced restructuring):
 * - mergeAdjacentRules: Combine adjacent CSS rules with same selectors
 * - mergeIntoShorthands: Convert long-form properties to shorthand
 * - mergeMedia: Combine @media rules with same conditions
 * - mergeNonAdjacentRules: Merge duplicate rules even if not adjacent
 * - mergeSemantically: Disabled to avoid potentially breaking semantic merges
 * - overrideProperties: Remove overridden properties
 * - removeEmpty: Remove empty rules at level 2
 * - reducePadding: Optimize padding values
 * - reducePositions: Optimize position values
 * - reduceTimingFunctions: Optimize animation timing functions
 * - reduceTransforms: Optimize transform functions
 * - restructureRules: Disabled to avoid aggressive restructuring that might break CSS
 * - skipProperties: Empty array means optimize all properties
 *
 * Format options (output formatting):
 * - All breaks set to false: Output as single line (no line breaks)
 * - indentBy: 0 (no indentation for minimal file size)
 * - indentWith: &quot;space&quot; (if indentation were enabled)
 * - All spaces set to false: Remove unnecessary spaces
 * - wrapAt: false (no line wrapping)
 *
 * Other options:
 * - inline: [&quot;none&quot;] - Don&#39;t inline any @import rules
 * - rebase: false - Don&#39;t rebase URLs (keep original paths)
 * - returnPromise: false - Use synchronous API (we handle async ourselves)
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cleanCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CleanCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;cleanupCharsets&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;normalizeUrls&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeBackground&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeBorderRadius&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeFilter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeFont&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeFontWeight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;optimizeOutline&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeEmpty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeNegativePaddings&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeQuotes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeWhitespace&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;replaceMultipleZeros&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;replaceTimeUnits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;replaceZeroUnits&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;roundingPrecision&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;selectorsSortingMethod&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;standard&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;specialComments&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;tidyAtRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;tidyBlockScopes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;tidySelectors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token function function-variable&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;mergeAdjacentRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;mergeIntoShorthands&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;mergeMedia&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;mergeNonAdjacentRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;mergeSemantically&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;overrideProperties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;removeEmpty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;reducePadding&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;reducePositions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;reduceTimingFunctions&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;reduceTransforms&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;restructureRules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;skipProperties&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;breaks&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterAtRule&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterBlockBegins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterBlockEnds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterComment&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterProperty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterRuleBegins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;afterRuleEnds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;beforeBlockEnds&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;betweenSelectors&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;indentBy&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;indentWith&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;space&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;spaces&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;aroundSelectorRelation&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;beforeBlockBegins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token literal-property property&quot;&gt;beforeValue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token literal-property property&quot;&gt;wrapAt&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;inline&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;none&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;rebase&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;returnPromise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Default Brotli Compression Level
 *
 * Brotli compression levels range from 0-11:
 * - 0: Fastest, least compression
 * - 11: Slowest, best compression (maximum)
 *
 * Level 11 is used by default for production builds where file size matters
 * more than compression time. This can be overridden via BROTLI_COMPRESSION_LEVEL
 * environment variable.
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * In-Memory Build Cache
 *
 * This Map caches CSS processing results during a single build run.
 * It serves two purposes:
 * 1. Prevents re-processing the same CSS file when multiple pages reference it
 * 2. Handles concurrent requests by storing Promises during processing
 *
 * Structure:
 * - Key: cssPath (the original CSS file path)
 * - Value: Either a Promise (if processing is in progress) or an object with:
 *   - hash: Content hash of the CSS file
 *   - processedCSS: The minified CSS string
 *   - htmlOutput: The final HTML &amp;lt;link&gt; tag string
 *
 * This cache is cleared between builds (per-process, not persisted).
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cssBuildCache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Directory Creation Tracking
 *
 * Tracks which directory combinations have been created during this build.
 * This prevents redundant fs.existsSync() and fs.mkdirSync() calls when
 * multiple CSS files are processed.
 *
 * Structure: Set of strings like &quot;cacheDir:outputDir&quot;
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; directoriesCreated &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * Main Function: manipulateCSS
 *
 * Registers a custom Eleventy shortcode that processes CSS files.
 * The shortcode can be used in templates like: 
 *
 * Processing Pipeline:
 * 1. Check if in local development mode (skip processing)
 * 2. Check in-memory cache (avoid duplicate processing)
 * 3. Read and hash the CSS file
 * 4. Check disk cache for minified CSS
 * 5. Minify CSS if not cached
 * 6. Write processed CSS with hash-based filename
 * 7. Compress with Brotli
 * 8. Return HTML &amp;lt;link&gt; tag with hashed filename for cache busting
 *
 * @param {object} eleventyConfig - The Eleventy configuration object
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;manipulateCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;/**
	 * Register the &quot;customCSS&quot; shortcode with Eleventy
	 * This makes it available in all templates as 
	 *
	 * @param {string} cssPath - The relative path to the CSS file (from /public directory)
	 * @returns {Promise&amp;lt;string&gt;} - HTML string containing a &amp;lt;link&gt; tag pointing to the processed CSS
	 */&lt;/span&gt;
	eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;customCSS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;cssPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 1: Local Development Short-Circuit
		 *
		 * In local development, skip all processing to speed up builds.
		 * Just return a simple &amp;lt;link&gt; tag pointing to the original file.
		 * This allows for faster iteration during development.
		 */&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isLocal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 2: In-Memory Cache Check
		 *
		 * Check if we&#39;ve already processed this CSS file during this build.
		 * This handles the common case where multiple pages reference the same CSS file.
		 *
		 * The cache can contain:
		 * - A Promise: Processing is currently in progress, wait for it
		 * - An object: Processing is complete, return the cached HTML output
		 */&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssBuildCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cached &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cssBuildCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// If it&#39;s a promise (in progress), wait for it&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// This handles concurrent requests - multiple pages calling this shortcode&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// simultaneously for the same CSS file will all wait for the same processing&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cached &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// Otherwise return cached result immediately (already HTML string)&lt;/span&gt;
			&lt;span class=&quot;token comment&quot;&gt;// This is the fast path for subsequent page builds&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; cached&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;htmlOutput&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 3: Initialize Processing
		 *
		 * Set up file paths and configuration for processing.
		 */&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// Construct full paths for input/output/cache directories&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputFile &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cssPath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Source CSS file location&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; outputDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./_site&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Where processed CSS goes (build output)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./.cache&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;css&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Where minified CSS cache is stored&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 4: Get Brotli Compression Level
		 *
		 * Read compression level from environment variable or use default.
		 * This allows fine-tuning compression vs. speed trade-off per environment.
		 * Higher levels = better compression but slower processing.
		 */&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliCompressionLevel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;BROTLI_COMPRESSION_LEVEL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;DEFAULT_BROTLI_COMPRESSION_LEVEL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Base 10 parsing&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 5: Create Processing Promise
		 *
		 * Wrap all processing in an async IIFE (Immediately Invoked Function Expression).
		 * This allows us to:
		 * 1. Handle concurrent requests by caching the Promise itself
		 * 2. Catch errors at the processing level
		 * 3. Return the same Promise to multiple concurrent callers
		 */&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; processingPromise &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.1: Validate Input File Exists
				 *
				 * Check if the source CSS file exists before attempting to process it.
				 * If missing, log an error and return empty string (fails gracefully).
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.2: Ensure Directories Exist
				 *
				 * Create output and cache directories if they don&#39;t exist.
				 * Uses a Set to track which directory combinations have been created
				 * during this build to avoid redundant checks and operations.
				 *
				 * recursive: true ensures parent directories are created if needed.
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dirKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;outputDirectory&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;directoriesCreated&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;has&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dirKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dir &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
							fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mkdirSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dir&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;recursive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
						&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
					directoriesCreated&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dirKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.3: Read and Hash CSS File
				 *
				 * Read the source CSS file and generate a content-based hash.
				 * The hash is used for:
				 * - Cache busting (filename changes when content changes)
				 * - Disk cache key (identify if we&#39;ve seen this content before)
				 *
				 * SHA-256 is used for strong collision resistance.
				 * Only first 10 characters of hash are used (sufficient for cache busting).
				 *
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; inputCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hash &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; crypto
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createHash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;sha256&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Use SHA-256 algorithm&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Hash the CSS content&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;digest&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hex&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Get hexadecimal representation&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Take first 10 chars (sufficient for uniqueness)&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.4: Generate Cache Key and Path
				 *
				 * Create a unique cache key combining:
				 * - The content hash (identifies file contents)
				 * - The file path (normalized to avoid path separator issues)
				 *
				 * This allows different CSS files with same content to share cache,
				 * but also handles edge cases where paths matter.
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;[/&#92;&#92;]&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachePath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cacheDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cacheKey&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.5: Minify CSS (or Load from Disk Cache)
				 *
				 * Check if we&#39;ve already minified this exact CSS content before.
				 * Disk cache persists between builds, so unchanged CSS files don&#39;t
				 * need re-minification even after restarting the build process.
				 *
				 * If cached:
				 * - Load the pre-minified CSS from disk
				 * - Skip the expensive minification step
				 *
				 * If not cached:
				 * - Run CleanCSS minification (expensive operation)
				 * - Save result to disk cache for next time
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// Cache hit - load pre-minified CSS&lt;/span&gt;
					processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token comment&quot;&gt;// Cache miss - minify and save&lt;/span&gt;
					processedCSS &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cleanCSS&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;minify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;styles&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cachePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.6: Write Processed CSS to Output Directory
				 *
				 * Write the minified CSS to the build output directory with a
				 * hash-based filename. The hash in the filename enables:
				 * - Cache busting (browser cache invalidates when content changes)
				 * - Long-term caching (files with same hash are unchanged)
				 *
				 * Filename format: original-name-hash.css
				 * Example: main-a1b2c3d4e5.css
				 *
				 * Only write if file doesn&#39;t exist to avoid redundant disk I/O
				 * (useful when multiple pages reference the same CSS).
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parsedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;inputFile&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Parse original filename&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; finalFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
					outputDirectory&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hash&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;parsedPath&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ext&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// name-hash.css&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.7: Brotli Compression
				 *
				 * Compress the minified CSS using Brotli algorithm.
				 * Brotli provides better compression than gzip, especially for text.
				 *
				 * The .br extension indicates Brotli-compressed files.
				 * Web servers can serve these directly to browsers that support Brotli
				 * (most modern browsers do via Accept-Encoding header).
				 *
				 * Compression happens synchronously (brotliCompressSync) because:
				 * - It&#39;s fast enough for build-time processing
				 * - Simplifies error handling
				 * - Build processes typically prefer synchronous operations
				 *
				 * Only compress if file doesn&#39;t exist (avoid redundant compression).
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliFilename &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;finalFilename&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.br&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Add .br extension&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;existsSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
						&lt;span class=&quot;token literal-property property&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; brotliCompressionLevel&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Compression level (0-11)&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; brotliBuffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;brotliCompressSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
						Buffer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;processedCSS&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Convert string to Buffer&lt;/span&gt;
						brotliOptions&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
					&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;promises&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;brotliFilename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; brotliBuffer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.8: Generate Final HTML Output
				 *
				 * Create the HTML &amp;lt;link&gt; tag that will be inserted into the page.
				 * The path is relative to the site root and uses forward slashes
				 * (normalized for web URLs, works on all platforms).
				 *
				 * Note: The HTML references the .br file, assuming the web server
				 * can serve Brotli-compressed files when the browser supports it.
				 * If your server doesn&#39;t handle .br files, you may need to modify
				 * this to point to the uncompressed file or configure your server.
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; hashedPath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; brotliFilename
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;./_site&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Remove build directory prefix&lt;/span&gt;
					&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token regex&quot;&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token language-regex regex-source&quot;&gt;&#92;&#92;&lt;/span&gt;&lt;span class=&quot;token regex-delimiter&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token regex-flags&quot;&gt;g&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Normalize path separators for web URLs&lt;/span&gt;

				&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;${&lt;/span&gt;hashedPath&lt;span class=&quot;token punctuation interpolation-punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token string template-punctuation&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Stage 5.9: Cache Result in Memory
				 *
				 * Store the processing result in the in-memory cache for subsequent
				 * calls during this build. This includes:
				 * - hash: For reference/debugging
				 * - processedCSS: In case we need the CSS string later
				 * - htmlOutput: The final HTML tag (what we return)
				 *
				 * Replace the Promise in cache with the actual result object.
				 */&lt;/span&gt;
				cssBuildCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; hash&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processedCSS&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;htmlOutput&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; result &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token comment&quot;&gt;/**
				 * Error Handling
				 *
				 * Catch any errors during processing and fail gracefully.
				 * Return empty string to prevent one CSS file error from breaking the entire build.
				 */&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;/**
		 * Stage 6: Handle Concurrent Requests
		 *
		 * Store the processing Promise in the cache BEFORE awaiting it.
		 * This ensures that if multiple pages call this shortcode simultaneously
		 * for the same CSS file, they all get the same Promise and wait for
		 * the same processing to complete (no duplicate work).
		 *
		 * Once processing completes, the Promise in cache is replaced with
		 * the result object (see Stage 5.9).
		 */&lt;/span&gt;
		cssBuildCache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cssPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; processingPromise&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; processingPromise&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It&#39;s all a little clumsy to have the code only in the blog post, so I&#39;ve created a &lt;a href=&quot;https://gist.github.com/Nooshu/edfc2e382bc249e92ab238779357c93e&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gist on GitHub here&lt;/a&gt; for easier reading &amp; copying and pasting (should you wish to use or modify it yourself).&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;&lt;p&gt;So how would you use this code in 11ty? Let&#39;s go through each of the files you&#39;d need to modify.&lt;h3 id=&quot;eleventy-config-js&quot;&gt;eleventy.config.js&lt;/h3&gt;&lt;p&gt;Just your standard ESM 11ty config file with an import and the execution.&lt;pre class=&quot;language-js&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Custom CSS manipulation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; manipulateCSS &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;./_helpers/css-manipulation.js&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;eleventyConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// All your other 11ty config above...&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// execute the CSS manipulation&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;manipulateCSS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// All your other 11ty config below...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;env&quot;&gt;.env&lt;/h3&gt;&lt;p&gt;It sits in the 11ty root directory and is added to &lt;code&gt;.gitignore&lt;/code&gt;.&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;ELEVENTY_ENV=development&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;html-template&quot;&gt;HTML template&lt;/h3&gt;&lt;p&gt;I, personally, have my HTML &lt;code&gt;&amp;lt;head&gt;&lt;/code&gt; separate and sitting in its own Nunjucks partial, but it will work with any template setup really. This Shortcode is using Nunjucks because that&#39;s what I use on this blog. Spaces added in the Nunjucks to stop 11ty just printing the output. The &lt;code&gt;index.css&lt;/code&gt; is my one and only CSS file I use on this blog, and it is passed into the Shortcode. It is either used unmodified (in development), or manipulated (in production).&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	{ % customCSS &quot;/css/index.css&quot; % }
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;output&quot;&gt;Output&lt;/h3&gt;&lt;p&gt;For my development environment the HTML output is:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There&#39;s no fingerprinting until it is built on production (in my case Cloudflare Pages). Where the output looks like this:&lt;pre class=&quot;language-html&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/css/index-b9fcfe85ef.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As you can see the &lt;code&gt;index.css&lt;/code&gt; file that contains all the CSS for this site, now has a unique 10 character hash suffix. If I were to change a single byte of data in the CSS file it will generate an entirely new hash. For those wondering, a sha256 hash using only the first 10 characters still has 1,099,511,627,776 unique combinations, so the likelihood of a collision is pretty slim!&lt;p&gt;Since the name of my CSS file is now guaranteed to have a unique name I can use the following response headers for my CSS:&lt;h3 id=&quot;headers-file&quot;&gt;_headers file&lt;/h3&gt;&lt;p&gt;We can now start to use &lt;code&gt;Cache-Control&lt;/code&gt; directives that maximise the time that the CSS is stored in a user&#39;s browser cache.&lt;pre class=&quot;language-txt&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;/[css]/*
Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we are telling the user&#39;s browser that:&lt;ol&gt;&lt;li&gt;&lt;strong&gt;public&lt;/strong&gt;: This response can be cached by any cache, it isn&#39;t restricted to only a private cache (like a user&#39;s browser cache). Examples of other caches are CDNs, and proxies.&lt;li&gt;&lt;strong&gt;max-age=31536000&lt;/strong&gt;: 31536000 seconds is the number of seconds in a year. So, this directive tells caches that they can store and reuse this response for up to a year without the need for revalidation.&lt;li&gt;&lt;strong&gt;immutable&lt;/strong&gt;: This is a hint to the browser or cache system that the content will never change for the lifetime of the cache. Even if the user reloads the page, the browser won&#39;t bother checking with the server for updates.&lt;/ol&gt;&lt;p&gt;There&#39;s a lot involved in caching on the web, so if you want to know a lot more about this fascinating topic, check out &lt;a href=&quot;https://csswizardry.com/2019/03/cache-control-for-civilians/&quot;&gt;Cache-Control for Civilians by Harry Roberts&lt;/a&gt;.&lt;p&gt;For those wondering about what happens to the old &quot;orphaned&quot; files in the cache, in this instance:&lt;ul&gt;&lt;li&gt;The old file version will stay in the cache for up to 1 year (until the cache duration expires).&lt;li&gt;But it&#39;s more than likely that the browser will clean up the cache before the year expiry time to free up storage space. This is more likely on devices with limited resources like older phones, since devices with limited resources will need to be more aggressive with their resource management.&lt;li&gt;In either case, this isn&#39;t anything a user has to worry about. The browsers will take care of this automatically, although a user can manually clear their browser cache should they wish too.&lt;/ul&gt;&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;&lt;p&gt;There we go, another 11ty blog post! We&#39;ve reviewed my current CSS setup, which works well for now, but as mentioned earlier, feel free to suggest improvements or point out anything that strays from the &quot;11ty way&quot;. As, the path to true enlightenment starts with uncovering the unseen gaps in our (my) understanding. I read that last sentence in Master Yoda&#39;s voice! As always, thanks for taking the time to read the post. I hope you found it useful and informative, and if you have any comments or feedback, please do &lt;a href=&quot;https://nooshu.com/contact/&quot;&gt;let me know&lt;/a&gt;.&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Post changelog:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;12/01/25: Initial post published.&lt;li&gt;02/11/25: Updated post and code after shortcode was found to run for every page generation in the build process. This was inefficient and is now limited to a single run per build.&lt;/ul&gt;</content>
  </entry>
</feed>