You are browsing the new, beta version of my website. Some things may not work properly. If you spot any problems, please file an issue!
38 posts 2009 16 posts 2010 50 posts 2011 28 posts 2012 15 posts 2013 7 posts 2014 10 posts 2015 5 posts 2016 4 posts 2017 7 posts 2018 2 posts 2019 17 posts 2020 7 posts 2021 7 posts 2022 8 posts 2023

On Yak Shaving and <md-block>, a new HTML element for Markdown

2 min read 0 comments

This week has been Yak Shaving Galore. It went a bit like this:

  1. I’ve been working on a web component that I need for the project I’m working on. More on that later, but let’s call it <x-foo> for now.
  2. Of course that needs to be developed as a separate reusable library and released as a separate open source project. No, this is not the titular component, this was only level 1 of my multi-level yak shaving… 🤦🏽‍♀️
  3. I wanted to showcase various usage examples of that component in its page, so I made another component for these demos: <x-foo-live>. This demo component would have markup with editable parts on one side and the live rendering on the other side.
  4. I wanted the editable parts to autosize as you type. Hey, I’ve written a library for that in the past, it’s called Stretchy!
  5. But Stretchy was not written in ESM, nor did it support Shadow DOM. I must rewrite Stretchy in ESM and support Shadow DOM first! Surely it won’t take more than a half hour, it’s a tiny library.
  6. (It took more than a half hour)
  7. Ok, now I have a nice lil’ module, but I also need to export IIFE as well, so that it’s compatible with Stretchy v1. Let’s switch to Rollup and npm scripts and ditch Gulp.
  8. Oh look, Stretchy’s CSS is still written in Sass, even though it doesn’t really need it now. Let’s rewrite it to use CSS variables, use PostCSS for nesting, and use conic-gradient() instead of inline SVG data URIs.
  9. Ok, Stretchy v2 is ready, now I need to update its docs. Oooh, it doesn’t have a README? I should add one. But I don’t want to duplicate content between the page and the README. Hmmm, if only…
  10. I know! I’ll make a web component for rendering both inline and remote Markdown! I have an unfinished one lying around somewhere, surely it won’t take more than a couple hours to finish it?
  11. (It took almost a day, two with docs, demos etc)
  12. Done! Here it is!
  13. Great! Now I can update Stretchy’s docs and release its v2
  14. Great! Now I can use Stretchy in my <x-foo-live> component demoing my <x-foo> component and be back to only one level of yak shaving!
  15. Wow, it’s already Friday afternoon?! 🤦🏽‍♀️😂

Hopefully you find useful! Enjoy!

Custom properties with defaults: 3+1 strategies

6 min read 0 comments

When developing customizable components, one often wants to expose various parameters of the styling as custom properties, and form a sort of CSS API. This is still underutlized, but there are libraries, e.g. Shoelace, that already list custom properties alongside other parts of each component’s API (even CSS parts!).

Note: I’m using “component” here broadly, as any reusable chunk of HTML/CSS/JS, not necessarily a web component or framework component. What we are going to discuss applies to reusable chunks of HTML just as much as it does to “proper” web components.

Let’s suppose we are designing a certain button styling, that looks like this:

We want to support a --color custom property for creating color variations by setting multiple things internally:

.fancy-button {
	border: .1em solid var(--color);
	background: transparent;
	color: var(--color);

.fancy-button:hover {
	background: var(--color);
	color: white;

Note that with the code above, if no --color is set, the three declarations using it will be IACVT and thus we’ll get a nearly unstyled text-only button with no background on hover (transparent), no border on hover, and the default black text color (canvastext to be precise).

That’s no good! IT’s important that we set defaults. However, using the fallback parameter for this gets tedious, and WET:

Continue reading

Inherit ancestor font-size, for fun and profit

9 min read 0 comments

If you’ve been writing CSS for any length of time, you’re probably familiar with the em unit, and possibly the other type-relative units. We are going to refer to em for the rest of this post, but anything described works for all type-relative units.

As you well know, em resolves to the current font size on all properties except font-size, where it resolves to the parent font size. It can be quite useful for making scalable components that adapt to their context size.

However, I have often come across cases where you actually need to “circumvent” one level of this. Either you need to set font-size to the grandparent font size instead of the parent one, or you need to set other properties to the parent font size, not the current one.

If you’re already familiar with the problem and just want the solution, skip ahead. The next few paragraphs are for those thinking “but when would you ever need this?”

Sometimes, there are workarounds, and it’s just a matter of keeping DRY. For example, take a look at this speech bubble:

Continue reading

Is the current tab active?

6 min read 0 comments

Today I ran into an interesting problem. Interesting because it’s one of those very straightforward, deceptively simple questions, that after a fair amount of digging, does not appear to have a definite answer (though I would love to be wrong!).

The problem was to determine if the current tab is active. Yes, as simple as that.

Why? (i.e. my use case)

I was working on my slide deck framework, Inspire.js. There is a presenter mode plugin, which spawns a new window with your slides (“projector view”), whereas your current window becomes a “presenter view”, with open notes, preview of the next slide, optional progress indicator for time etc.

However, this plugin was not very good. The two windows are synced, but only if you use presenter view to navigate slides. If you use the projector view to advance slides, the syncing breaks. Why would you use the projector mode? Many reasons, e.g. to interact with a live demo, or even play a video. If you have a live demo heavy presentation, you may even want to mirror your screen and only ever interact with the projector mode, while having the presenter mode on a secondary screen, just to look at.

The way the plugin worked was that every time the slide changed in the presenter view, it propagated the change in the projector view. To make the syncing bidirectional, it would be good to know if the current window is the active tab, and if so, propagate all slide navigation to the other one, regardless of which one is the projector view and which one is the presenter view.

And this, my friends, is how I ended up in this rabbit hole.

(Yes, there are other solutions to this particular problem. I could just always propagate regardless and have checks in place to avoid infinite loops. But that’s beside the point.)

Continue reading

82% of developers get this 3 line CSS quiz wrong

3 min read 0 comments

(I always wanted to do a clickbait title like this and when this chance came along I could not pass it up. 😅 Sorry!)

While putting my ideas into slides for my Dynamic CSS workshop for next week, I was working on a slide explaining how the CSS wide keywords work with custom properties. inherit, initial, unset I had used numerous times and knew well. But what about revert? How did that work? I had an idea, but quickly coded up a demo to try it out.

The code was:

:root {
    --accent-color: skyblue;

div {
    --accent-color: revert;
    background: var(--accent-color, orange);

Phew, I was correct, but the amount of uncertainty I had before seeing the result tipped me that I might be on to something.

Before you read on, take a moment to think about what you would vote. Warning: Spoilers ahead!





Continue reading

Dark mode in 5 minutes, with inverted lightness variables

9 min read 0 comments

By now, you probably know that you can use custom properties for individual color components, to avoid repeating the same color coordinates multiple times throughout your theme. You may even know that you can use the same variable for multiple components, e.g. HSL hue and lightness:

:root {
	--primary-hs: 250 30%;

h1 {
	color: hsl(var(--primary-hs) 30%);

article {
	background: hsl(var(--primary-hs) 90%);

article h2 {
	background: hsl(var(--primary-hs) 40%);
	color: white;

Here is a very simple page designed with this technque:

Unlike preprocessor variables, you could even locally override the variable, to have blocks with a different accent color:

:root {
	--primary-hs: 250 30%;
	--secondary-hs: 190 40%;

article {
	background: hsl(var(--primary-hs) 90%);

article.alt {
	--primary-hs: var(--secondary-hs);

This is all fine and dandy, until dark mode comes into play. The idea of using custom properties to make it easier to adapt a theme to dark mode is not new. However, in every article I have seen, the strategy suggested is to create a bunch of custom properties, one for each color, and override them in a media query.

This is a fine approach, and you’ll likely want to do that for at least part of your colors eventually. However, even in the most disciplined of designs, not every color is a CSS variable. You often have colors declared inline, especially grays (e.g. the footer color in our example). This means that adding a dark mode is taxing enough that you may put it off for later, especially on side projects.

The trick I’m going to show you will make anyone who knows enough about color cringe (sorry Chris!) but it does help you create a dark mode that works in minutes. It won’t be great, and you should eventually tweak it to create a proper dark mode (also dark mode is not just about swapping colors) but it’s better than nothing and can serve as a base.

Continue reading

Mass function overloading: why and how?

6 min read 0 comments

One of the things I’ve been doing for the past few months (on and off—more off than on TBH) is rewriting Bliss to use ESM 1. Since Bliss v1 was not using a modular architecture at all, this introduced some interesting challenges.

Continue reading

Writable getters

7 min read 0 comments

Setters removing themselves are reminiscent of Ouroboros, the serpent eating its own tail, an ancient symbol. Media credit

A pattern that has come up a few times in my code is the following: an object has a property which defaults to an expression based on its other properties unless it’s explicitly set, in which case it functions like a normal property. Essentially, the expression functions as a default value.

Some examples of use cases:

  • An object where a default id is generated from its name or title, but can also have custom ids.
  • An object with information about a human, where name can be either specified explicitly or generated from firstName and lastName if not specified.
  • An object with parameters for drawing an ellipse, where ry defaults to rx if not explicitly set.
  • An object literal with date information, and a readable property which formats the date, but can be overwritten with a custom human-readable format.
  • An object representing parts of a Github URL (e.g. username, repo, branch) with an apiCall property which can be either customized or generated from the parts (this is actually the example which prompted this blog post)

Ok, so now that I convinced you about the utility of this pattern, how do we implement it in JS?

Continue reading

Position Statement for the 2020 W3C TAG Election

7 min read 0 comments

Update: I got elected!! Thank you so much to every W3C member organization who voted for me. 🙏🏼 Now on to making the Web better, alongside fellow TAG members!

Context: I’m running for one of the four open seats in this year’s W3C TAG election. The W3C Technical Architecture Group (TAG) is the Working Group that ensures that Web Platform technologies are usable and follow consistent design principles, whether they are created inside or outside W3C. It advocates for the needs of everyone who uses the Web and everyone who works on the Web. If you work for a company that is a W3C Member, please consider encouraging your AC rep to vote for me! My candidate statement follows.

Hi, I’m Lea Verou. Equally at home in Web development, the standards process, and programming language design, I bring a rarely-found cross-disciplinary understanding of the full stack of front-end development.

I have a thorough and fundamental understanding of all the core technologies of the Web Platform: HTML, CSS, JS, DOM, and SVG. I bring the experience and perspective of having worked as a web designer & developer in the trenches — not in large corporate systems, but on smaller, independent projects for clients, the type of projects that form the majority of the Web. I have started many open source projects, used on millions of websites, large and small. Some of my work has been incorporated in browser dev tools, and some has helped push CSS implementations forwards.

However, unlike most web developers, I am experienced in working within W3C, both as a longtime member of the CSS Working Group, as well as a W3C Staff alumnus. This experience has given me a fuller grasp of Web technology development: not just the authoring side, but also the needs and constraints of implementation teams, the kinds of problems that tend to show up in our work, and the design principles we apply. I understand in practice how the standards process at W3C addresses the problems and weighs up the necessary compromises — from high-level design changes to minute details — to create successful standards for the Web.

I have spent over six years doing PhD research at MIT on the intersection of programming language design and human-computer interaction. My research has been published in top-tier peer-reviewed academic venues.  My strong usability background gives me the ability to identify API design pitfalls early on in the design process.

In addition, I have been teaching web technologies for over a decade, both to professional web developers, through my numerous talks, workshops, and bestselling book, and as an instructor and course co-creator for MIT. This experience helps me to easily identify aspects of API design that can make a technology difficult to learn and conceptualize.

If elected, I will work with the rest of the TAG to:

  • Ensure that web technologies are not only powerful, but also learnable and approachable, with a smooth ease-of-use to complexity curve.
  • Ensure that where possible, commonly needed functionality is available through approachable declarative HTML or CSS syntax and not solely through JS APIs.
  • Work towards making the Web platform more extensible, to allow experienced developers to encapsulate complexity and make it available to novice authors, empowering the latter to create compelling content. Steps have been made in this direction with Web Components and the Houdini specifications, but there are still many gaps that need to be addressed.
  • Record design principles that are often implicit knowledge in standards groups, passed on but never recorded. Explicit design principles help us keep technologies internally consistent, but also assist library developers who want to design APIs that are consistent with the Web Platform and feel like a natural extension of it. A great start has been made with the initial drafts of the Design Principles document, but there is still a lot to be done.
  • Guide those seeking TAG review, some of whom may be new to the standards process, to improve their specifications.

Having worn all these hats, I can understand and empathize with the needs of designers and developers, authors and implementers, practitioners and academics, putting me in a unique position to help ensure the Web Platform remains consistent, usable, and inclusive.

I would like to thank Open JS Foundation and Bocoup for graciously funding my TAG-related travel, in the event that I am elected.

Selected endorsements

Tantek Çelik, Mozilla’s AC representative, longtime CSS WG member, and creator of many popular technologies:

I have had the privilege of working with Lea in the CSS Working Group, and in the broader web development community for many years. Lea is an expert in the practical real-world-web technologies of the W3C, how they fit together, has put them into practice, has helped contribute to their evolution, directly in specs and in working groups. She’s also a passionate user & developer advocate, both of which I think are excellent for the TAG.


Florian Rivoal, CSS WG Invited Expert and editor of several specifications, elected W3C AB member, ex-Opera:

Elika Etemad aka fantasai, prolific editor of dozens of W3C specifications, CSS WG member for over 16 years, and elected W3C AB member:

One TPAC long ago, several members of the TAG on a recruiting spree went around asking people to run for the TAG. I personally turned them down for multiple reasons (including that I’m only a very poor substitute for David Baron), but it occurred to me recently that there was a candidate that they do need: Lea Verou.

Lea is one of those elite developers whose technical expertise ranges across the entire Web platform. She doesn’t just use HTML, CSS, JS, and SVG, she pushes the boundaries of what they’re capable of. Meanwhile her authoring experience spans JS libraries to small site design to CSS+HTML print publications, giving her a personal appreciation of a wide variety of use cases. Unlike most other developers in her class, however, Lea also brings her experience working within W3C as a longtime member of the CSS Working Group.

I’ve seen firsthand that she is capable of participating at the deep and excruciatingly detailed level that we operate here, and that her attention is not just on the feature at hand but also the system and its usability and coherence as a whole. She knows how the standards process works, how use cases and implementation constraints drive our design decisions, and how participation in the arcane discussions at W3C can make a real difference in the future usability of the Web.

I’m recommending her for the TAG because she’s able to bring a perspective that is needed and frequently missing from our technical discussions which so often revolve around implementers, and because elevating her to TAG would give her both the opportunity and the empowerment to bring that perspective to more of our Web technology development here at W3C and beyond.


Bruce Lawson, Opera alumni, world renowned accessibility expert, speaker, author:

Brian Kardell, AC representative for both Open JS Foundation and Igalia:

The OpenJS Foundation is very pleased to nominate and offer our support for Lea Verou to the W3C TAG. We believe that she brings a fresh perspective, diverse background and several kinds of insight that would be exceptionally useful in the TAG’s work.


Lea Verou is another easy choice for me. Lea brings a really diverse background, set of perspectives and skills to the table. She’s worked for the W3C, she’s a great communicator to developers (this is definitely a great skill in TAG whose outreach is important), she’s worked with small teams, produced a number of popular libraries and helped drive some interesting standards. The OpenJS Foundation was pleased to nominate her, but Frontiers and several others were also supportive. Lea also deserves “high marks”.


The case for Weak Dependencies in JS

8 min read 0 comments

Earlier today, I was briefly entertaining the idea of writing a library to wrap and enhance querySelectorAll in certain ways. I thought I’d rather not introduce a Parsel dependency out of the box, but only use it to parse selectors properly when it’s available, and use more crude regex when it’s not (which would cover most use cases for what I wanted to do).

In the olden days, where every library introduced a global, I could just do:

if (window.Parsel) {
	let ast = Parsel.parse();
	// rewrite selector properly, with AST
else {
	// crude regex replace

However, with ESM, there doesn’t seem to be a way to detect whether a module is imported, without actually importing it yourself.

I tweeted about this…

I thought this was a common paradigm, and everyone would understand why this was useful. However, I was surprised to find that most people were baffled about my use case. Most of them thought I was either talking about conditional imports, or error recovery after failed imports.

I suspect it might be because my primary perspective for writing JS is that of a library author, where I do not control the host environment, whereas for most developers, their primary perspective is that of writing JS for a specific app or website.

After Kyle Simpson asked me to elaborate about the use case, I figured a blog post was in order.

The use case is essentially progressive enhancement (in fact, I toyed with the idea of titling this blog post “Progressively Enhanced JS”). If library X is loaded already by other code, do a more elaborate thing and cover all the edge cases, otherwise do a more basic thing. It’s for dependencies that are not really dependencies, but more like nice-to-haves.

Continue reading

Simple pie charts with fallback, today

5 min read 0 comments

Five years ago, I had written this extensive Smashing Magazine article detailing multiple different methods for creating simple pie charts, either with clever use of transforms and pseudo-elements, or with SVG stroke-dasharray. In the end, I mentioned creating pie charts with conic gradients, as a future technique. It was actually a writeup of my “The Missing Slice” talk, and an excerpt of my CSS Secrets book, which had just been published.

I was reminded of this article today by someone on Twitter:\_kent\_/status/1326805431390531584

I suggested conic gradients, since they are now supported in >87% of users’ browsers, but he needed to support IE11. He suggested using my polyfill from back then, but this is not a very good idea today.

Indeed, unless you really need to display conic gradients, even I would not recommend using the polyfill on a production facing site. It requires -prefix-free, which re-fetches (albeit from cache) your entire CSS and sticks it in a <style> element, with no sourcemaps since those were not a thing back when -prefix-free was written. If you’re already using -prefix-free, the polyfill is great, but if not, it’s way too heavy a dependency.

Pie charts with fallback (modern browsers)

Instead, what I would recommend is graceful degradation, i.e. to use the same color stops, but in a linear gradient.

We can use @supports and have quite an elaborate progress bar fallback. For example, take a look at this 40% pie chart:

.pie {
	height: 20px;
	background: linear-gradient(to right, deeppink 40%, transparent 0);
	background-color: gold;

@supports (background: conic-gradient(white, black)) { .pie { width: 200px; height: 200px; background-image: conic-gradient(deeppink 40%, transparent 0); border-radius: 50%; } }

This is what it looks like in Firefox 82 (conic gradients are scheduled to ship unflagged in Firefox 83) or IE11:

Note that because @supports is only used for the pie and not the fallback, the lack of IE11 support for it doesn’t affect us one iota.

If relatively modern browsers are all we care about, we could even use CSS variables for the percentage and the color stops, to avoid duplication, and to be able to set the percentage from the markup:

<div class="pie" style="--p: 40%"></div>
.pie {
	height: 20px;
	--stops: deeppink var(--p, 0%), transparent 0;
	background: linear-gradient(to right, var(--stops));
	background-color: gold;

@supports (background: conic-gradient(white, black)) { .pie { width: 200px; height: 200px; background-image: conic-gradient(var(–stops)); border-radius: 50%; } }

You can use a similar approach for 3 or more segments, or for a vertical bar.

One issue with this approach is that our layout needs to work well with two charts of completely different proportions. To avoid that, we could just use a square:

.pie {
	width: 200px;
	height: 200px;
	background: linear-gradient(to right, deeppink 40%, transparent 0) gold;

@supports (background: conic-gradient(white, black)) { .pie { background-image: conic-gradient(deeppink 40%, transparent 0); border-radius: 50%; } }

which produces this in IE11:

Granted, a square progress bar is not the same, but it can still convey the same relationship and is easier to design a layout around it since it always has the same aspect ratio.

Why not use radial gradients?

You might be wondering, why not just use a radial gradient, which could use the same dimensions and rounding. Something like this:

There are two problems with this. The first one may be obvious: Horizontal or vertical bars are common for showing the proportional difference between two amounts, albeit less good than a pie chart because it’s harder to compare with 50% at a glance (yes Tufte, pie charts can be better for some things!). Such circular graphs are very uncommon. And for good reason: Drawn naively (e.g. in our case if the radius of the pink circle is 40% of the radius of the yellow circle), their areas do not have the relative relationship we want to depict.

Why is that? Let r be the radius of the yellow circle. As we know from middle school, the area of the entire circle is π_r_², so the area of the yellow ring is π_r_² - (area of pink circle). The area of the pink circle is π(0.4_r_)² = 0.16π_r_². Therefore, the area of the yellow ring is π_r_² - 0.16π_r_² = 0.84π_r_² and their relative ratio is 0.16π_r_² / 0.84π_r_² = 0.16 / 0.84 ≅ 0.19 which is a far cry from the 40/60 (≅ 0.67) we were looking for!

Instead, if we wanted to draw a similar visualization to depict the correct relationship, we need to start from the ratio and work our way backwards. Let r be the radius of the yellow circle and kr the radius of the pink circle. Their ratio is π(kr)² / (π_r_² - π(kr)²) = 4/6 ⇒ _k_² / (1 - _k_²) = 4/6 ⇒ (1 - _k_²) / _k_² = 6/4 ⇒ 1/_k_² - 1 = 6/4 ⇒ 1/_k_² = 10/4 ⇒ k = 2 / sqrt(10) ≅ .632 Therefore, the radius of the pink circle should be around 63.2% of the radius of the yellow circle, and a more correct chart would look like this:

In the general case where the pink circle is depicting the percentage p, we’d want the radius of the pink circle to be sqrt(1 / p) the size of the yellow circle. That’s a fair bit of calculations that we can’t yet automate (though sqrt() is coming!). Moral of the story: use a bar as your fallback!

The -​-var: ; hack to toggle multiple values with one custom property

4 min read 0 comments

What if I told you you could use a single property value to turn multiple different values on and off across multiple different properties and even across multiple CSS rules?

What if I told you you could turn this flat button into a glossy skeuomorphic button by just tweaking one custom property --is-raised, and that would set its border, background image, box and text shadows in one fell swoop?

How, you may ask?

The crux of this technique is this: There are two custom property values that work almost everywhere there is a var() call with a fallback.

The more obvious one that you probably already know is the initial value, which makes the property just apply its fallback. So, in the following code:

background: var(--foo, linear-gradient(white, transparent)) hsl(220 10% 50%);
border: 1px solid var(--foo, rgb(0 0 0 / .1));
color: rgb(0 0 0 var(--foo, / .8));

We can set --foo to initial to enable these “fallbacks” and append these values to the property value, adding a gradient, setting a border-color, and making the text color translucent in one go. But what to do when we want to turn these values off? Any non-initial value for --foo (that doesn’t create cycles) should work. But is there one that works in all three declarations?

It turns out there is another value that works everywhere, in every property a var() reference is present, and you’d likely never guess what it is (unless you have watched any of my CSS variable talks and have a good memory for passing mentions of things).


It’s whitespace! Whitespace is significant in a custom property. When you write something like this:

--foo: ;

This is not an invalid declaration. This is a declaration where the value of --foo is literally one space character. However, whitespace is valid in every CSS property value, everywhere a var() is allowed, and does not affect its computed value in any way. So, we can just set our property to one space (or even a comment) and not affect any other value present in the declaration. E.g. this:

--foo: ;
background: var(--foo, linear-gradient(white, transparent)) hsl(220 10% 50%);

produces the same result as:

background: hsl(220 10% 50%);

We can take advantage of this to essentially turn var() into a single-clause if() function and conditionally append values based on a single custom property.

As a proof of concept, here is the two button demo refactored using this approach:


I originally envisioned this as a building block for a technique horrible hack to enable “mixins” in the browser, since @apply is now defunct. However, the big limitation is that this only works for appending values to existing values — or setting a property to either a whole value or initial. There is no way to say “the background should be red if --foo is set and white otherwise”. Some such conditionals can be emulated with clever use of appending, but not most.

And of course there’s a certain readability issue: --foo: ; looks like a mistake and --foo: initial looks pretty weird, unless you’re aware of this technique. However, that can easily be solved with comments. Or even constants:

:root {
	--ON: initial;
	--OFF: ;

button { –is-raised: var(–OFF); /* … */ }

#foo { –is-raised: var(–ON); }

Also do note that eventually we will get a proper if() and won’t need such horrible hacks to emulate it, discussions are already underway [w3c/csswg-drafts#5009 w3c/csswg-drafts#4731].

So what do you think? Horrible hack, useful technique, or both? 😀

Prior art

Turns out this was independently discovered by two people before me:

And it was called “space toggle hack” in case you want to google it!

The failed promise of Web Components

6 min read 0 comments

Web Components had so much potential to empower HTML to do more, and make web development more accessible to non-programmers and easier for programmers. Remember how exciting it was every time we got new shiny HTML elements that actually do stuff? Remember how exciting it was to be able to do sliders, color pickers, dialogs, disclosure widgets straight in the HTML, without having to include any widget libraries?

The promise of Web Components was that we’d get this convenience, but for a much wider range of HTML elements, developed much faster, as nobody needs to wait for the full spec + implementation process. We’d just include a script, and boom, we have more elements at our disposal!

Or, that was the idea. Somewhere along the way, the space got flooded by JS frameworks aficionados, who revel in complex APIs, overengineered build processes and dependency graphs that look like the roots of a banyan tree.

This is what the roots of a Banyan tree look like. Photo by David Stanley on Flickr (CC-BY).

Perusing the components on fills me with anxiety, and I’m perfectly comfortable writing JS — I write JS for a living! What hope do those who can’t write JS have? Using a custom element from the directory often needs to be preceded by a ritual of npm flugelhorn, import clownshoes, build quux, all completely unapologetically because “here is my truckload of dependencies, yeah, what”. Many steps are even omitted, likely because they are “obvious”. Often, you wade through the maze only to find the component doesn’t work anymore, or is not fit for your purpose.

Besides setup, the main problem is that HTML is not treated with the appropriate respect in the design of these components. They are not designed as closely as possible to standard HTML elements, but expect JS to be written for them to do anything. HTML is simply treated as a shorthand, or worse, as merely a marker to indicate where the element goes in the DOM, with all parameters passed in via JS. I recall a wonderful talk by Jeremy Keith a few years ago about this very phenomenon, where he discussed this e-shop Web components demo by Google, which is the poster child of this practice. These are the entire contents of its <body> element:

	<shop-app unresolved="">SHOP</shop-app>
	<script src="node_assets/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>
	<script type="module" src="src/shop-app.js"></script>

If this is how Google is leading the way, how can we hope for contributors to design components that follow established HTML conventions?

Jeremy criticized this practice from the aspect of backwards compatibility: when JS is broken or not enabled, or the browser doesn’t support Web Components, the entire website is blank. While this is indeed a serious concern, my primary concern is one of usability: HTML is a lower barrier to entry language. Far more people can write HTML than JS. Even for those who do eventually write JS, it often comes after spending years writing HTML & CSS.

If components are designed in a way that requires JS, this excludes thousands of people from using them. And even for those who can write JS, HTML is often easier: you don’t see many people rolling their own sliders or using JS-based ones once <input type="range"> became widely supported, right?

Even when JS is unavoidable, it’s not black and white. A well designed HTML element can reduce the amount and complexity of JS needed to a minimum. Think of the <dialog> element: it usually does require *some* JS, but it’s usually rather simple JS. Similarly, the <video> element is perfectly usable just by writing HTML, and has a comprehensive JS API for anyone who wants to do fancy custom things.

The other day I was looking for a simple, dependency free, tabs component. You know, the canonical example of something that is easy to do with Web Components, the example 50% of tutorials mention. I didn’t even care what it looked like, it was for a testing interface. I just wanted something that is small and works like a normal HTML element. Yet, it proved so hard I ended up writing my own!

Can we fix this?

I’m not sure if this is a design issue, or a documentation issue. Perhaps for many of these web components, there are easier ways to use them. Perhaps there are vanilla web components out there that I just can’t find. Perhaps I’m looking in the wrong place and there is another directory somewhere with different goals and a different target audience.

But if not, and if I’m not alone in feeling this way, we need a directory of web components with strict inclusion criteria:

  • Plug and play. No dependencies, no setup beyond including one <script> tag. If a dependency is absolutely needed (e.g. in a map component it doesn’t make sense to draw your own maps), the component loads it automatically if it’s not already loaded.
  • Syntax and API follows conventions established by built-in HTML elements and anything that can be done without the component user writing JS, is doable without JS, per the W3C principle of least power.
  • Accessible by default via sensible ARIA defaults, just like normal HTML elements.
  • Themable via ::part(), selective inheritance and custom properties. Very minimal style by default. Normal CSS properties should just “work” to the the extent possible.
  • Only one component of a given type in the directory, that is flexible and extensible and continuously iterated on and improved by the community. Not 30 different sliders and 15 different tabs that users have to wade through. No branding, no silos of “component libraries”. Only elements that are designed as closely as possible to what a browser would implement in every way the current technology allows.

I would be up for working on this if others feel the same way, since that is not a project for one person to tackle. Who’s with me?

UPDATE: Wow this post blew up! Thank you all for your interest in participating in a potential future effort. I’m currently talking to stakeholders of some of the existing efforts to see if there are any potential collaborations before I go off and create a new one. Follow me on Twitter to hear about the outcome!

Developer priorities throughout their career

4 min read 0 comments

I made this chart in the amazing Excalidraw about two weeks ago:

It only took me 10 minutes! Shortly after, my laptop broke down into repeated kernel panics, and it spent about 10 days in service (I was in a remote place when it broke, so it took some time to get it to service). Yesterday, I was finally reunited with it, turned it on, launched Chrome, and saw it again. It gave me a smile, and I realized I never got to post it, so I tweeted this:

The tweet kinda blew up! It seems many, many developers identify with it. A few also disagreed with it, especially with the “Does it actually work?” line. So I figured I should write a bit about the rationale behind it. I originally wrote it in a tweet, but then I realized I should probably post it in a less transient medium, that is more well suited to longer text.

When somebody starts coding, getting the code to work is already difficult enough, so there is no space for other priorities. Learning to formalize one’s thought to the degree a computer demands, and then serialize this thinking with an unforgiving syntax, is hard. Writing code that works is THE priority, and whether it’s good code is not even a consideration.

For more experienced programmers, whether it works is ephemeral: today it works, tomorrow a commit causes a regression, the day after another commit fixes it (yes, even with TDD. No testsuite gets close to 100% coverage). Whereas readability & maintainability do not fluctuate much. If they are not prioritized from the beginning, they are much harder to accomplish when you already have a large codebase full of technical debt.

Code written by experienced programmers that doesn’t work, can often be fixed with hours or days of debugging. A nontrivial codebase that is not readable can take months or years to rewrite. So one tends to gravitate towards prioritizing what is easier to fix.

The “peak of drought” and other over-abstractions

Many developers identified with the “peak of drought”. Indeed, like other aspects of maintainability, DRY is not even a concern at first. At some point, a programmer learns about the importance of DRY and gradually begins abstracting away duplication. However, you can have too much of a good thing: soon the need to abstract away any duplication becomes all consuming and leads to absurd, awkward abstractions which actually get in the way and produce needless couplings, often to avoid duplicating very little code, once. In my own “peak of drought” (which lasted far longer than the graph above suggests), I’ve written many useless functions, with parameters that make no sense, just to avoid duplicating a few lines of code once.

Many articles have been written about this phenomenon, so I’m not going to repeat their arguments here. As a programmer accumulates even more experience, they start seeing the downsides of over-abstraction and over-normalization and start favoring a more moderate approach which prioritizes readability over DRY when they are at odds.

A similar thing happens with design patterns too. At some point, a few years in, a developer reads a book or takes a course about design patterns. Soon thereafter, their code becomes so littered with design patterns that it is practically incomprehensible. “When all you have is a hammer, everything looks like a nail”. I have a feeling that Java and Java-like languages are particularly accommodating to this ailment, so this phenomenon tends to proliferate in their codebases. At some point, the developer has to go back to their past code, and they realize themselves that it is unreadable. Eventually, they learn to use design patterns when they are actually useful, and favor readability over design patterns when the two are at odds.

What aspects of your coding practice have changed over the years? How has your perspective shifted? What mistakes of the past did you eventually realize?

Parsel: A tiny, permissive CSS selector parser

4 min read 0 comments

I’ve posted before about my work for the Web Almanac this year. To make it easier to calculate the stats about CSS selectors, we looked to use an existing selector parser, but most were too big and/or had dependencies or didn’t account for all selectors we wanted to parse, and we’d need to write our own walk and specificity methods anyway. So I did what I usually do in these cases: I wrote my own!

You can find it here:

It not only parses CSS selectors, but also includes methods to walk the AST produced, as well as calculate specificity as an array and convert it to a number for easy comparison.

It is one of my first libraries released as an ES module, and there are instructions about both using it as a module, and as a global, for those who would rather not deal with ES modules yet, because convenient as ESM are, I wouldn’t want to exclude those less familiar with modern JS.

Please try it out and report any bugs! We plan to use it for Almanac stats in the next few days, so if you can spot bugs sooner rather than later, you can help that volunteer effort. I’m primarily interested in (realistic) valid selectors that are parsed incorrectly. I’m aware there are many invalid selectors that are parsed weirdly, but that’s not a focus (hence the “permissive” aspect, there are many invalid selectors it won’t throw on, and that’s by design to keep the code small, the logic simple, and the functionality future-proof).

How it works

If you’re just interested in using this selector parser, read no further. This section is about how the parser works, for those interested in this kind of thing. :)

I first started by writing a typical parser, with character-by-character gobbling and different modes, with code somewhat inspired by my familiarity with jsep. I quickly realized that was a more fragile approach for what I wanted to do, and would result in a much larger module. I also missed the ease and flexibility of doing things with regexes.

However, since CSS selectors include strings and parens that can be nested, parsing them with regexes is a fool’s errand. Nested structures are not regular languages as my CS friends know. You cannot use a regex to find the closing parenthesis that corresponds to an opening parenthesis, since you can have other nested parens inside it. And it gets even more complex when there are other tokens that can nest, such as strings or comments. What if you have an opening paren that contains a string with a closing paren, like e.g. ("foo)")? A regex would match the closing paren inside the string. In fact, parsing the language of nested parens (strings like (()(()))) with regexes is one of the typical (futile) exercises in a compilers course. Students struggle to do it because it’s an impossible task, and learn the hard way that not everything can be parsed with regexes.

Unlike a typical programming language with lots of nested structures however, the language of CSS selectors is more limited. There are only two nested structures: strings and parens, and they only appear in specific types of selectors (namely attribute selectors, pseudo-classes and pseudo-elements). Once we get those out of the way, everything else can be easily parsed by regexes. So I decided to go with a hybrid approach: The selector is first looked at character-by-character, to extract strings and parens. We only extract top-level parens, since anything inside them can be parsed separately (when it’s a selector), or not at all. The strings are replaced by a single character, as many times as the length of the string, so that any character offsets do not change, and the strings themselves are stored in a stack. Same with parens.

After that point, this modified selector language is a regular language that can be parsed with regexes. To do so, I follow an approach inspired by the early days of Prism: An object literal of tokens in the order they should be matched in, and a function that tokenizes a string by iteratively matching tokens from an object literal. In fact, this function was taken from an early version of Prism and modified.

After we have the list of tokens as a flat array, we can restore strings and parens, and then nest them appropriately to create an AST.

Also note that the token regexes use the new-ish named capture groups feature in ES2018, since it’s now supported pretty widely in terms of market share. For wider support, you can transpile :)