Reading List

The most recent articles from a list of feeds I subscribe to.

Writable getters

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?

Our first attempt may look something like this:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	}
}

Note: We are going to use object literals in this post for simplicity, but the same logic applies to variations using Object.create(), or a class Person of which lea is an instance.

Our first attempt doesn’t quite work as you might expect:

lea.id; // "lea-verou"
lea.id = "lv";
lea.id; // Still "lea-verou"!

Why does this happen? The reason is that the presence of the getter turns the property into an accessor, and thus, it cannot also hold data. If it doesn’t have a setter, then simply nothing happens when it is set.

However, we can have a setter that, when invoked, deletes the accessor and replaces it with a data property:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	},
	set id(v) {
		delete this.id;
		return this.id = v;
	}
}

Abstracting the pattern into a helper

If we find ourselves needing this pattern in more than one places in our codebase, we could abstract it into a helper:

function writableGetter(o, property, getter, options = {}) {
	Object.defineProperty(o, property, {
		get: getter,
		set (v) {
			delete this[property];
			return this[property] = v;
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Note that we used Object.defineProperty() here instead of the succinct get/set syntax. Not only is the former more convenient for augmenting pre-existing objects, but also it allows us to customize enumerability, while the latter just defaults to enumerable: true.

We’d use the helper like this:

let lea = {name: "Lea Verou"};
writableGetter(lea, "id", function() {
	return this.name.toLowerCase().replace(/\W+/g, "-");
}, {enumerable: false});

Overwriting the getter with a different getter

This works when we want to overwrite with a static value, but what if we want to overwrite with a different getter? For example, consider the date use case: what if we want to maintain a single source of truth for the date components and only overwrite the format, as a function, so that when the date components change, the formatted date updates accordingly?

If we are confident that setting the property to an actual function value wouldn’t make sense, we could handle that case specially, and create a new getter instead of a data property:

function writableGetter(o, property, getter, options = {}) {
	return Object.defineProperty(o, property, {
		get () {
			return getter.call(this);
		},
		set (v) {
			if (typeof v === "function") {
				getter = v;
			}
			else {
				delete this[property];
				return this[property] = v;
			}
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Do note that if we set the property to a static value, and try to set it to a function after that, it will just be a data property that creates a function, since we’ve deleted the accessor that handled functions specially. If that is a significant concern, we can maintain the accessor and just update the getter:

function writableGetter(o, property, getter, options = {}) {
	return Object.defineProperty(o, property, {
		get () {
			return getter.call(this);
		},
		set (v) {
			if (typeof v === "function") {
				getter = v;
			}
			else {
				getter = () => v;
			}
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Improving the DX of our helper

While this was the most straightforward way to define a helper, it doesn’t feel very natural to use. Our object definition is now scattered in multiple places, and readability is poor. This is often the case when we start implementing before designing a UI. In this case, writing the helper is the implementation, and its calling code is effectively the UI.

It’s always a good practice to start designing functions by writing a call to that function, as if a tireless elf working for us had already written the implementation of our dreams.

So how would we prefer to write our object? I’d actually prefer to use the more readable get() syntax, and have everything in one place, then somehow convert that getter to a writable getter. Something like this:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	}
}
makeGetterWritable(lea, "id", {enumerable: true});

Can we implement something like this? Of course. This is JS, we can do anything!

The main idea is that we read back the descriptor our get syntax created, fiddle with it, then stuff it back in as a new property:

function makeGetterWritable(o, property, options) {
	let d = Object.getOwnPropertyDescriptor(o, property);
	let getter = d.get;

	d.get = function() {
		return getter.call(this);
	};

	d.set = function(v) {
		if (typeof v === "function") {
			getter = v;
		}
		else {
			delete this[property];
			return this[property] = v;
		}
	};

	// Apply any overrides, e.g. enumerable
	Object.assign(d, options);

	// Redefine the property with the new descriptor
	Object.defineProperty(o, property, d)
}

Other mixed data-accessor properties

While JS is very firm in its distinction of accessor properties and data properties, the reality is that we often need to combine the two in different ways, and conceptually it’s more of a data-accessor spectrum than two distinct categories. Here are a few more examples where the boundary between data property and accessor property is somewhat …murky:

  • “Live” data properties: properties which execute code to produce side effects when they are get or set, but still hold data like a regular data property. This can be faked by having a helper that creates a hidden data property. This idea is the core of Bliss.live().
  • Lazy evaluation: Properties which are evaluated when they are first read (via a getter), then replace themselves with a regular data property. If they are set before they are read, they function exactly like a writable getter. This idea is the core of Bliss.lazy(). MDN mentions this pattern too.

Note: Please don’t actually implement id/slug generation with name.toLowerCase().replace(/\W+/g, "-"). That’s very simplistic, to keep examples short. It privileges English/ASCII over other languages and writing systems, and thus, should be avoided.

Writable getters

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?

Our first attempt may look something like this:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	}
}

Note: We are going to use object literals in this post for simplicity, but the same logic applies to variations using Object.create(), or a class Person of which lea is an instance.

Our first attempt doesn’t quite work as you might expect:

lea.id; // "lea-verou"
lea.id = "lv";
lea.id; // Still "lea-verou"!

Why does this happen? The reason is that the presence of the getter turns the property into an accessor, and thus, it cannot also hold data. If it doesn’t have a setter, then simply nothing happens when it is set.

However, we can have a setter that, when invoked, deletes the accessor and replaces it with a data property:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	},
	set id(v) {
		delete this.id;
		return this.id = v;
	}
}

Abstracting the pattern into a helper

If we find ourselves needing this pattern in more than one places in our codebase, we could abstract it into a helper:

function writableGetter(o, property, getter, options = {}) {
	Object.defineProperty(o, property, {
		get: getter,
		set (v) {
			delete this[property];
			return this[property] = v;
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Note that we used Object.defineProperty() here instead of the succinct get/set syntax. Not only is the former more convenient for augmenting pre-existing objects, but also it allows us to customize enumerability, while the latter just defaults to enumerable: true.

We’d use the helper like this:

let lea = {name: "Lea Verou"};
writableGetter(lea, "id", function() {
	return this.name.toLowerCase().replace(/\W+/g, "-");
}, {enumerable: false});

Overwriting the getter with a different getter

This works when we want to overwrite with a static value, but what if we want to overwrite with a different getter? For example, consider the date use case: what if we want to maintain a single source of truth for the date components and only overwrite the format, as a function, so that when the date components change, the formatted date updates accordingly?

If we are confident that setting the property to an actual function value wouldn’t make sense, we could handle that case specially, and create a new getter instead of a data property:

function writableGetter(o, property, getter, options = {}) {
	return Object.defineProperty(o, property, {
		get () {
			return getter.call(this);
		},
		set (v) {
			if (typeof v === "function") {
				getter = v;
			}
			else {
				delete this[property];
				return this[property] = v;
			}
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Do note that if we set the property to a static value, and try to set it to a function after that, it will just be a data property that creates a function, since we’ve deleted the accessor that handled functions specially. If that is a significant concern, we can maintain the accessor and just update the getter:

function writableGetter(o, property, getter, options = {}) {
	return Object.defineProperty(o, property, {
		get () {
			return getter.call(this);
		},
		set (v) {
			if (typeof v === "function") {
				getter = v;
			}
			else {
				getter = () => v;
			}
		},
		enumerable: true,
		configurable: true,
		...options
	});
}

Improving the DX of our helper

While this was the most straightforward way to define a helper, it doesn’t feel very natural to use. Our object definition is now scattered in multiple places, and readability is poor. This is often the case when we start implementing before designing a UI. In this case, writing the helper is the implementation, and its calling code is effectively the UI.

It’s always a good practice to start designing functions by writing a call to that function, as if a tireless elf working for us had already written the implementation of our dreams.

So how would we prefer to write our object? I’d actually prefer to use the more readable get() syntax, and have everything in one place, then somehow convert that getter to a writable getter. Something like this:

let lea = {
	name: "Lea Verou",
	get id() {
		return this.name.toLowerCase().replace(/\W+/g, "-");
	}
}
makeGetterWritable(lea, "id", {enumerable: true});

Can we implement something like this? Of course. This is JS, we can do anything!

The main idea is that we read back the descriptor our get syntax created, fiddle with it, then stuff it back in as a new property:

function makeGetterWritable(o, property, options) {
	let d = Object.getOwnPropertyDescriptor(o, property);
	let getter = d.get;

	d.get = function() {
		return getter.call(this);
	};

	d.set = function(v) {
		if (typeof v === "function") {
			getter = v;
		}
		else {
			delete this[property];
			return this[property] = v;
		}
	};

	// Apply any overrides, e.g. enumerable
	Object.assign(d, options);

	// Redefine the property with the new descriptor
	Object.defineProperty(o, property, d)
}

Other mixed data-accessor properties

While JS is very firm in its distinction of accessor properties and data properties, the reality is that we often need to combine the two in different ways, and conceptually it’s more of a data-accessor spectrum than two distinct categories. Here are a few more examples where the boundary between data property and accessor property is somewhat …murky:

  • “Live” data properties: properties which execute code to produce side effects when they are get or set, but still hold data like a regular data property. This can be faked by having a helper that creates a hidden data property. This idea is the core of Bliss.live().
  • Lazy evaluation: Properties which are evaluated when they are first read (via a getter), then replace themselves with a regular data property. If they are set before they are read, they function exactly like a writable getter. This idea is the core of Bliss.lazy(). MDN mentions this pattern too.

Note: Please don’t actually implement id/slug generation with name.toLowerCase().replace(/\W+/g, "-"). That’s very simplistic, to keep examples short. It privileges English/ASCII over other languages and writing systems, and thus, should be avoided.

Position Statement for the 2020 W3C TAG Election

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 […]

Position Statement for the 2020 W3C TAG Election

Reading Time: 5 minutes 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 […]

Position Statement for the 2020 W3C TAG Election

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.

Source: https://lists.w3.org/Archives/Member/w3c-ac-forum/2021JanMar/0015.html

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

https://twitter.com/frivoal/status/1336857605063417856

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.

Source: https://lists.w3.org/Archives/Member/w3c-ac-forum/2020OctDec/0055.html

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

https://twitter.com/brucel/status/1336260046691438594

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.

Source: https://www.w3.org/2020/12/07-tag-nominations#lv

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”.

Source: https://bkardell.com/blog/TAG-2021.html