Obi-Wan Kenobi on Disney+

Looking forward to this one:

The story begins 10 years after the dramatic events of “Star Wars: Revenge of the Sith” where Obi-Wan Kenobi faced his greatest defeat—the downfall and corruption of his best friend and Jedi apprentice, Anakin Skywalker, who turned to the dark side as evil Sith Lord Darth Vader.

Airing starts next week, May 27

First Look At The CSS object-view-box Property

Ahmad Shadeed dives into the new object-view-box property, a side-invention to the aforementioned Page Transitions API:

The object-view-box property specifies a “view box” over an element, similar to the <svg viewBox> attribute, zooming or panning over the element’s contents.

Like so:

img {
    aspect-ratio: 1;
    width: 300px;
    object-view-box: inset(25% 20% 15% 0%);

Available now in Chrome canary.

First Look At The CSS object-view-box Property →

Bringing Page Transitions to the Web

At this year’s Google I/O, Jake presented the revised version of the Transition API:

Native apps often feature transitions between states that both look great and help communicate the type of navigation to the user. The bad news: creating transitions between pages on the web is impossible. In-page transitions are possible, but complex. The good news: new APIs are coming to simplify this process, building on top of CSS animations and the web animation API, and it works across navigations!

Very excited about this one, as this new version is way more powerful than its first iteration (which I also played with around this time last year).

You can try it out in Chrome Canary with the chrome://flags/#document-transition feature flag enabled.

Developer Guide →
Demo →

Deep Dive into Text Wrapping and Word Breaking

Will Boyd:

Let’s talk about the various ways we can control how text wraps (or doesn’t wrap) on a web page. CSS gives us a lot of tools to make sure our text flows the way we want it to, but we’ll also cover some tricks using HTML and special characters.

Contains a ton of demos covering overflow-wrap, word-break, hyphens, white-space, …

Deep Dive into Text Wrapping and Word Breaking →

Max Cooper – Symphony in Acid

Embedded above is the video clip for Max Cooper’s Symphony in Acid

This video is from my ‘Unspoken Words’ project, where I tried to express things with music and visual art which I could not put into words. I turned to the writing of Ludwig Wittgenstein, who tackled this issue of the problems with words and their inability to capture every aspect of our lives. It approaches issues of human expression and the links between our symbolism and existence, but does so in an incomprehensibly dense way, for me at least, which was ripe for presenting as art rather than a technical reading challenge. It’s full of feeling and suggestion that we’ve played with here.

Yes, you can actually visit that website and have it play the “video clip”. Hit space to pause the track at any time, q to randomize the layout, and any of the w,e,r,t keys to alter some of the highlights. Uses a huge – manual – mapping of audio frames to visual frames and p5 under the hood.

There’s also something mesmerizing about watching the DevTools’s Elements panel and see the DOM changes get highlighted.

Symphony in Acid →

Progressive Enhancement and HTML Forms: use FormData

Progressive Enhancement and HTML Forms
Progressive Enhancement and HTML Forms (Base Illustration by Shopify)

In my article “Embrace the Platform” over at CSS-Tricks I mentioned this experience by Drew Devault:

My web browser has been perfectly competent at submitting HTML forms for the past 28 years, but for some stupid reason some developer decided to reimplement all of the form semantics in JavaScript, and now I can’t pay my electricity bill without opening up the dev tools.

While the article hints at solving the situation using Progressive Enhancement, it doesn’t cover the practical side of things. Let’s change that with this post.



To apply Progressive Enhancement on HTML forms, there’s this little JavaScript gem named FormData that one can use:

The FormData interface provides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the fetch() or XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".

To use FormData, create a new instance of it and pass in a form element as its first argument. It will automagically do its thing.

const form = document.querySelector('form');
const data = new FormData(form); // 👈 Captures all the form’s key-value pairs

As FormData captures all the form’s key-value pairs, applying Progressive Enhancement on forms becomes pretty easy:

  1. Build a regular HTML form that submits its data to somewhere
  2. Make it visually interesting with CSS
  3. Using JavaScript, hijack the form’s submit event and, instead, send its contents — captured through FormData — using fetch() to the defined endpoint.

A first iteration of the JavaScript code for step 3 would look like this:

document.querySelector("form").addEventListener("submit", async (event) => {

  const form = event.currentTarget;
  const resource = form.action;
  const options = {
    method: form.method,
    body: new FormData(form) // 👈 Magic!

  const r = await fetch(resource, options);

  if (!r.ok) {
    // @TODO: Show an error message

  // @TODO: handle the response by showing a message, redirecting, etc.

Congratulations, you’ve just progressively enhanced your form!


Dealing with JSON and GET

While this basic approach already works, it doesn’t cover all scenarios, mainly due to the way how FormData encodes the data:

The FormData interface […] uses the same format a form would use if the encoding type were set to "multipart/form-data".

So if you want to send the data as JSON, you will need to convert the formData to it yourself. Thankfully, using Modern JavaScript, that’s only a one-liner nowadays. Furthermore the code also doesn’t properly handle GET requests. To cater for those one doesn’t need to send the formData as the request body, but alter the resource’s query string parameters instead.

Expressed in code, we need these adjustments:

const resource = new URL(form.action || window.location.href);

// …

if (options.method === "get") { = new URLSearchParams(formData);
} else {
  if (form.enctype === "multipart/form-data") {
    options.body = formData;
  } else {
    options.body = JSON.stringify(Object.fromEntries(formData));
    options.headers['Content-Type'] = 'application/json';



Embedded below is a CodePen demo that submits the data to a dummy API and then handles its response:

See the Pen
Progressive Enhancement and <form>: Use FormData
by Bramus (@bramus)
on CodePen.



Next steps

The core of this post evolves around using FormData + fetch() to capture and send the data. As for next steps, the experience can still further be improved by – for example – preventing double form submissions and showing a loading spinner.

Example of a loading indicator while the form is submitting


🔥 Like what you see? Want to stay in the loop? Here's how:

The C in CSS (2022.04.20 @ Full Stack Ghent)

Yesterday I attended the April 2022 “Full Stack Ghent” Meetup, at the offices of In The Pocket. The evening consisted of 8 lightning talks. Being part of the speaker line-up, I kicked off the meetup with a talk titled “The C in CSS”.

CSS is short for “Cascading Style Sheets”. But what exactly is this cascade, and how does it work? Let this talk enlighten you.

The slides are up on, and also embedded below:

ℹ️ The talk is a (heavily) trimmed down version of a full talk in the CSS Cascade, which I’ll be giving at several upcoming conferences (Web Directions Hover, CSS Day, …)


I was very happy to attend this meetup — my first (public) in-person one after a very long time — especially as it had been canceled two times already (in as many years) due to COVID. I don’t think I’m alone in this, given that there was a turnout of ±130 (!) people.


Thanks to the organizers for having me, and thanks to the other speakers as well for giving their talks. It was a varied mix of frontend, backend, ops, and even RegExes. I hope you all had fun attending my talk — I know I had making it (and whilst bringing it forward) — and perhaps you even learned something from it along the way 🙂

💁‍♂️ If you are a conference or meetup organiser, don't hesitate to contact me to come speak at your event.

The Future of CSS: CSS Toggles

Late last week, I was very delighted to see that Chromium will start prototyping with CSS Toggles, a proposal currently only available as an Unofficial Draft, authored by Tab Atkins and Miriam Suzanne (who else?!).

CSS toggles are a mechanism for associating toggleable state with a DOM element. This state can be defined and connected to activations through CSS properties, and can be queried in CSS selectors. This provides a declarative mechanism for specifying state that describes behaviors like the existing HTML behaviors for checkboxes or radio buttons.

The goal of this prototype phase is to experiment with the idea, verify if things would work out or not, polish the syntax, etc. — so yes, all this is still conceptual, highly experimental, and subject to change at the time of writing. Even though still a very early WIP, I already like where this is headed.


The basic use-case would be something that resembles a light switch, which can go from 0 (inactive) to 1 (active)

html {
  toggle-root: lightswitch; /* Create a toggle named lightswitch. It will cycle between 0 (inactive, default) and 1 (active) */

button {
  toggle-trigger: lightswitch; /* When clicking the button, toggle the lightswitch */

html:toggle(lightswitch) {
  /* Styles to apply when the lightswitch toggle is active */

Here’s a demo of this behavior, which uses a JS-polyfill:

See the Pen
CSS Toggles Example
by Bramus (@bramus)
on CodePen.

🤔 Polyfill?

A polyfill is a piece of code (or plugin) that provides the technology that you, the developer, expect the browser to provide natively — What is a Polyfill?


But there’s a lot more to the spec than just this basic use case:

  • A Toggle Root can host more than one toggle
  • A toggle can have more than 1 active state, so it’s not only 0 and 1
  • The states don’t need to be numbers, but can also be a set of words
  • The initial toggle state is 0 by default, but you can override that
  • An element can be both the toggle-root and the toggle-trigger. In that case, use the toggle property
  • The scope of a toggle value can be narrowed down to descendant elements only (instead of being visible to siblings and their descendants)
  • toggle-visibility allows you to show/hide an element based on a toggle value. Think of details/summary and scenarios where you would rely on the checkbox checkbox hack. Benefit to using toggle-visibility is that the contents of the element are always available to the UA for in-page-find and the like (cfr. content-visibility)
  • Toggles can be grouped using toggle-group. Handy for tab interfaces, where only 1 tab can be active at the same time.
  • Sticky toggles can not become inactive, there will always be one item active
  • State machines with transitions are being talked about

It’s pretty powerful stuff, which will take some time to wrap your head around. For me, it clicked once I saw Miriam’s demo site in action, which uses a polyfill to make things work.

Very curious to see where this headed …


CSS Toggles Explainer →
CSS Toggles Demo Page (with Polyfill)
CSS Toggles (Unofficial Draft) →
Draft Spec Issues (GitHub) →

CSS Toggles Polyfill (GitHub) →
CSS WG Issue #6991: CSS Toggles →


🔥 Like what you see? Want to stay in the loop? Here's how:

Daft Punk – Around The World (In The Classroom with Michel Gondry)

This year, Daft Punk’s Homework turned 25. I was 13 when it was released, and it’s one of the music albums that influenced my musical taste a lot. In this video, Michel Gondry explains how the iconic video clip for “Around the World” is structured.

For reference, here’s the (remastered) music video clip.


I’m joining the Chrome Developer Relations team at Google

Good News Everyone!
Good news everyone!

Good news everyone! I’m joining Google! From early May on, I’ll be working with Una, Adam, Jecelyn, and Jhey as a “Developer Relations Engineer, Chrome and Developer Tools”.

Together, we’ll spread the word on CSS, Layout, Interactions, Animations, Typography, Chrome DevTools, etc.


Sharing my passion for the web (CSS specifically) is something that gives me joy. Writing technical articles, speaking at meetups and conferences, tweeting and retweeting useful demos/links/snippets, running workshops, … — all are things that I love to do.

Up until now this always has been an “after hours” type of thing for me, taking up a lot of my spare time. So it should be no surprise that I didn’t hesitate for a moment when I caught wind of this position. Being able to do what I love — advocating for the web — as my main job is a dream come true.

Oh, you betcha I’m adjusting accordingly!


My background as a College Lecturer will definitely come in handy here. Thanks to this past work experience, along with my current experience as a public speaker at meetups and conferences, I’m already familiar with creating courses and giving technical talks to audiences both big and small. As a Chrome DevRel Engineer, I can continue down this path and further polish these skills along the way.

Another continuation of things I’m already doing — and which soon will become part of my job — is writing technical articles, creating demos, experimenting with new CSS features, participating in CSS WG discussions, and filing bugs at the various Issue Trackers — only to name a few of my future tasks.

One of the new tasks that I’m really looking forward to is attending and participating in the weekly CSS WG Telecon. I don’t want to get ahead of things here, but you can bet on it that I’ll put work into the potential changes coming to Scroll-Linked Animations.


I intend to keep on publishing stuff here on, but can imagine that some posts — mainly about new CSS features — may end up on (or the like) instead. After all, I’d be writing those posts “on the job” then, instead of in my spare time now.

One side-effect of joining Google is that I will be shutting down my freelance business. I didn’t really ever plan on doing so — things are going great after all — but joining Una’s team is an opportunity I can’t turn down. As a result, I’m currently wrapping things up with my current clients. I hereby would like to thank them (along with my former clients) for the trust they’ve put in me over the past 15 years.

And oh, in case you’re wondering: me and a family will be staying in Belgium, as it’s a remote position. No changes there.


I’m very excited to join, and you can expect to hear more from me soon. First up: go through the (remote) onboarding process and whatnot 🙂


🔥 Like what you see? Want to stay in the loop? Here's how: