Gems in the rough #11

Dueling announcements, CSP-allowed CSS, fun with LQIPs.


Each entry in the “Gems in the rough” series is a collection of tips, explanations, and/or idle observations which I hope will be at least somewhat useful to those of you with websites built by static site generators (SSGs).

News from Vercel and Cloudflare

When this site is on Cloudflare Pages, as it is at this writing, it sits behind a Cloudflare Worker that provides the site a variety of features I couldn’t otherwise give it, such as on another host like Vercel. (More about that Worker further down.)

On October 26, Vercel announced it was introducing a new functionality, Edge Functions, which would work like Cloudflare Workers. This made perfect sense because, as Vercel personnel confirmed soon afterward, Edge Functions are based on Cloudflare Workers. At this writing, they remain (1.) in beta and (2.) available for only Next.js users; but both conditions should change soon.

Then, on November 17, Cloudflare had a “Hold my beer”-kinda announcement during its Full Stack Week event: it was making Cloudflare Pages a “full-stack” platform, with much tighter integration with a variety of Cloudflare products including, of course, the Workers product on which it always has been based. While this, too, is in beta at this writing, it does hold a gigantic amount of promise for enabling a lot more power, with a lot less pain (or futzing around, perhaps), in the CFP developer experience.

Dynamic styling and security

In two separate posts earlier this year, I gave you details about that aforementioned Cloudflare Worker. Specifically:

The tightness of that CSP prohibits something which can be useful: inline CSS styles. Earlier this week, I realized I had a particular need for dynamic styles (more on that shortly), so I added some code to the Worker for another nonce-related use. The following part of the Worker1 now makes it CSP-OK to add a style dynamically if the style has a nonce:

style-src 'nonce-${nonce}' 'self' https://* https://* data:;

As for why I wanted that dynamically added style, read on.

Enabling LQIP-based blur-up

When lazy-loading images, I like to provide that blur-up effect you often see with Gatsby, Next.js, and other platforms which utilize low-quality image placeholders (LQIPs). Essentially, you first provide a tiny version of the full image, expanded out to the full width of the div the image will occupy, thus producing a blurry starting item, and then fade in (“blur up”) the full image as it loads. This avoids a blank spot during the load, yet also provides a pleasing effect in the interim.

That’s easily done by having the full image housed within a div whose background is that LQIP. This typically is done with inline styling which changes dynamically on a per-image basis. I had once implemented this via the site’s imgc shortcode, whether in Eleventy or Hugo. However, I found that’s a no-go with a tight CSP — and now you understand my interest in that new nonce-handling in the Cloudflare Worker, which allowed me to add the following capabilities to imgc:

  • Use of MD5 generates a random hash of the image’s file name.
  • There’s now a dynamically generated class named imgB- followed by the hash. The class has only one item, specifying the LQIP2 as background-image for whatever uses the rule.
  • The wrapping div includes this class.

If you want to see the resulting HTML from how imgc works, use your browser’s View Source capability3 and see the code for the image below. Refresh the page and you’ll see that the imgB- rule’s nonce value changes each time. That’s the whole point, and it thus makes everything fine where the CSP’s style-src portion is concerned.

Photo of a cat named Shakespeare sitting on a window sill

Note from the future: See also “Better code for image processing in Hugo.”

  1. The part about https://* is because of the styling in the YouTube embeds↩︎

  2. Not the full image, as I erroneously stated in the initial publication of this post. ↩︎

  3. I say to use View Source rather than the Inspector because, on some browsers, using the Inspector won’t show you the nonce value. ↩︎