Gems in the rough #11

Dueling announcements, CSP-allowed CSS, fun with LQIPs

If you know what an “SSG” is, perhaps you’ll have interest in these items.

November 19, 2021
 

Colorful gemstones

Image: Sung Jin Cho; Unsplash

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://*.brycewray.com https://*.youtube-nocookie.com 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.js file, which works throughout the site as an Eleventy shortcode. 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.js:

  • The md5 package generates a random hash of the image’s file name.
  • There’s now a dynamically generated CSS 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.

The code for imgc.js is viewable on this site’s repo, specifically at this link. If you want to see the resulting HTML from how it works, use your browser’s Inspector tool on this post’s featured image. Refresh the page and you’ll see that the end of the imgB- rule changes each time. That’s the whole point of the nonce, thus making everything fine where the CSP’s style-src portion is concerned.


  1. The part about https://*.youtube-nocookie.com 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. ↩︎

Other posts

Next:

Previous: