Turbo: The speed of a single-page web application without having to write any JavaScript

From the folks at Basecamp comes Turbo:

Turbo accelerates links and form submissions without requiring you to change your server-side generated HTML. It lets you carve up a page into independent frames, which can be lazy-loaded and operate as independent components. And finally, helps you make partial page updates using just HTML and a set of CRUD-like container tags. These three techniques reduce the amount of custom JavaScript that many web applications need to write by an order of magnitude.

Nice, but that “without requiring you to change your server-side generated HTML” part is not exactly true though, as you have to sprinkle some turbo-* custom elements and data-turbo-* attributes all over your markup. Once those are in place Turbo will do its thing and progressively enhance the experience.

If you want to do more than the features provided by Turbo you can bundle it up with Stimulus and Strada. All three together are published under the name Hotwire.

Turbo →

Injecting a JavaScript Attack Vector using CSS Custom Properties

Earlier this week I saw this tweet by Sansec float by:

This one’s pretty nice I must say: as the syntax for CSS Custom Properties is overly permissive (see here) you can use Custom Properties to store your JavaScript attack vector in. If you then use window.getComputedStyle to extract the contents of the Custom Property (see here) and combine it with a function constructor and an IIFE, it’s possible to execute it.

Here’s a pen that loads a remote confetti script using the method described:

Let this underline the importance of a Content Security Policy to prevent remote script loading script evaluation.

Update: Blocking this “hack” with a proper CSP

It took me some time to figure out — as I’m no CSP expert — but turns out the unsafe-inline keyword in the CSP’s source list is enough to block the execution of the JS-IN-CSS.

As a reminder, here are the four allowed keywords:

  • 'none', as you might expect, matches nothing.
  • 'self' matches the current origin, but not its subdomains.
  • 'unsafe-inline' allows inline JavaScript and CSS.
  • 'unsafe-eval' allows text-to-JavaScript mechanisms like eval.

I first thought unsafe-inline would be insufficient here as the code does not call eval, but apparently a function constructor is (correctly!) considered equally harmful, and therefore also blocked.

Here’s an updated demo that blocks the script evaluation:

See the Pen
Injecting a JavaScript attack vector using CSS Custom Properties (with CSP)
by Bramus (@bramus)
on CodePen.

The CSP used is this one:

<meta
    http-equiv="Content-Security-Policy"
    content="script-src https://cpwebassets.codepen.io https://cdpn.io https://cdn.jsdelivr.net 'unsafe-inline';"
>

It works as follows:

  • https://cpwebassets.codepen.io and https://cdpn.io are there for the CodePen demo to work
  • https://cdn.jsdelivr.net is there to allow legitimate loading of scripts — such as a jQuery you might need — from that CDN.
  • unsafe-inline is the one that prevents the execution of the JS-IN-CSS defined script by blocking the call to the function constructor

That calls for confetti! 🤪

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

content-visiblity: auto; vs. jumpy scrollbars, a solution

As warned in content-visibility: the new CSS property that boosts your rendering performance you need to be careful with applying content-visibility: auto; on each and every element as the scrollbar might get jumpy.

This is because elements will be rendered as they scroll into the viewport and will be hidden as they scroll out of the viewport, thereby affecting the height of the rendered page, and thus also affecting the scrollbar.

ℹ️ Apart from a jumpy scrollbar it can also negatively affect accessibility when you include headings and landmark elements inside of regions styled with content-visibility: auto;. See Content-visibility and Accessible Semantics for details.

Now, thanks to infinite scroll we are — or at least I am — kind of used to the thumb part of the scrollbar shrinking and jumping back up a bit on the scrollbar track as you scroll down. What we’re not used to is the thumb part jump forwards on the scroll track as you scroll down. This is because elements that slide out of the viewport will no long be rendered — as that’s what content-visibility: auto; does — and the scrollbar is (by default) only calculated against the rendered elements.


Elements can become non-rendered elements as they scroll out of the viewport,
thanks to content-visibility: auto; doing its thing.

To cater for this jumpy behavior you should use contain-intrinsic-size so space for an element is reserved when it’s not being rendered. However, it is not always possible to know a box its dimensions in advance. Looking for a way to automatically reserve space for previously rendered elements, Alex Russel created a little script for it.

One challenge with naive application of content-visibility, though, is the way that it removes elements from the rendered tree once they leave the viewport — particularly as you scroll downward. If the scroll position depends on elements above the currently viewable content “accordion scrollbars” can dance gleefully as content-visibility: auto does its thing.

In a first version of the script he applied content-visibility: visible on each element from the moment it had appeared on screen. To detect this an IntersectionObserver is used. While this does prevent the scrollbar thumb from jumping forwards as you scroll down, it will make the page slow again as that content remains rendered (even though it’s off-screen).

A second version of the script takes a different approach and calculates the contain-intrinsic-size to apply based on the element’s dimensions. That way elements that passed by once now have a correct contain-intrinsic-size set, and can safely be hidden again as content-visibility: auto does its job.

let spaced = new WeakMap();
let reserveSpace = (el, rect) => {
    let old = spaced.get(el);
    // Set intrinsic size to prevent jumping.
    if (!old || rectNotEQ(old, rect)) {
        spaced.set(el, rect);
        el.attributeStyleMap.set(
        "contain-intrinsic-size",
        `${rect.width}px ${rect.height}px`
        );
    }
};

Additionally he also added a ResizeObserver to cater for resize events.

Resize-Resilient `content-visiblity` Fixes →

🤔 Clever script indeed, yet I cannot help but think: this should be possible without the needs for this extra script. What if a value like contain-intrinsic-size: auto; would be allowed, and do exactly as the script Alex built does?

UPDATE: I’ve created an issue on GitHub that proposes contain-intrinsic-size: auto;. Let’s see where this goes …

Deep Dive into Page Lifecycle API

As the name suggests, the Page Lifecycle API exposes the web page lifecycle hooks to JavaScript. However, it isn’t an entirely new concept. Page Visibility API existed for some time, revealing some of the page visibility events to JavaScript.
However, if you happen to choose between these two, it’s worth mentioning some of the limitations of the Page Visibility API.

  • It only provides visible and hidden states of a web page.
  • It can not capture pages discarded by the operating system (Android, IOS, and the latest Windows systems can terminated background processes to preserve system resources).

Let’s take a look at the page lifecycle states exposed by the Page Lifecycle API.

To implement the Page Lifecycle API and help overcome browser inconsistencies, there’s PageLifecycle.js that will come in handy:

import lifecycle from '/path/to/page-lifecycle.mjs';

lifecycle.addEventListener('statechange', function(event) {
  console.log(event.oldState, event.newState);
});

Deep Dive into Page Lifecycle API →
The Page Lifecycle API →
PageLifecycle.js →

Creating random-but-stable effects with the CSS Paint API

One of the side-effects when drawing things with a Houdini Paint Worklet and relying on Math.random() in your code, is that your layout might be jumpy.

Check out my CSS Houdini Paint Worklet that draws colorful circles for example: whenever you resize the available space or change one of its properties — or some of the other CSS properties — the circles are being drawn, at random places with random colors.

Sometimes this side-effect is unwanted: say you have a Paint Worklet that draws some random lines underneath an element. If you were to animate the --line-width on hover you don’t want it to draw different lines, but you want to keep the same lines in place, and only adjust their width.

To solve around this, Jake Archibald shows us how to implement mulberry32, which has consistent output no matter how many times you call it:

Computers can’t really do random. Instead, they take some state, and do some hot maths all over it to create a number. Then, they modify that state so the next number seems unrelated to the previous ones. But the truth is they’re 100% related.

If you start with the same initial state, you’ll get the same sequence of random numbers. That’s what we want – something that looks random, but it’s 100% reproducible.

Go check out the demos Jake has built. As per usual these are top-notch, and help explain everything well.

CSS paint API: Being predictably random →

Playing with JSX

In The several ways to import React, Kent C. Dodds also covers a bit about JSX and “the JSX pragma”. Here’s a video of CSS Tricks’ Chris Coyier goofing around with it.

JSX is not fancy. It essentially transforms angle brackets in JavaScript into function calls. That works great for React, but because we can customize it, we can make it work for other libraries or write our own.

💁‍♂️ You can see Chris importing React using the wonderful SkyPack there.

🔗 Related:

The several ways to import React

With the release of React 17 we also had to change the way we import React:

Kent C. Dodds goes over all ways to import React into your code, and explains why the good ole import React from "react" no longer works and why he went for import * as React from "react"

Importing React Through the Ages →

Skypack — Load optimized npm packages with no install and no build tools

I was delighted to read that CodePen now has built-in support for Skypack. This is a huge step forward to working with packages on CodePen. Great addition to the product!

But what exactly is Skypack? Well, it’s the successor to the aforementioned Pika CDN *COMBINED* with the aforementioned Snowpack: Skypack not only serves ES Modules, it also converts older CommonJS Modules to ES Modules while at it.

Ever tried to load JavaScript from a CDN and realized that it doesn’t work in a browser without a bundler? Skypack operates like your favorite CDN but with an important difference: packages are preoptimized for browser use.

But Skypack doesn’t stop there: it handles minification, browser polyfilling, gzip/brotli, HTTP/3, caching, and more!

Skypack is free to use for personal and commercial purposes, forever. The basic CDN is production-ready and is backed by Cloudflare, Google Cloud, and AWS. We’re fully committed to building a core piece of infrastructure you can rely on.

Fred K. Schott, one of its authors, announced it this summer at ESNext Conf — a virtual conference which I also gave a talk at.

(Fast forward to the 18 minute mark to get to the Skypack part)

Here’s a simple Confetti example:

See the Pen
Confetti
by Chris Coyier (@chriscoyier)
on CodePen.

Neat, right?

Skypack — Load optimized npm packages with no install and no build tools →
Skypack + CodePen →

(t,i,x,y) => “creative code golfing”

Martin Kleppe has created tixy.land, “the most minimalist creative coding environment” as he calls it

Control the size and color of a 16×16 dot matrix with a single JavaScript function. The input is limited to 32 characters – but no limits to your creativity!

I especially like how the site starts off with some simple examples that are directly rendered. Within a few clicks you’ll get why it’s called (t,i,x,y) 🙂

In a true code golfing style, Noah Doersing created (t,i,x,y,z), which adds a third dimension to it:

The best part of tixy is that your code snippet can be persisted in the URL, and therefore shared 🙂

(t,i,x,y)
(t,i,x,y,z)
(t,i,x,y,z) Source (GitHub) →