Categories
Articles Original Tutorials

Custom properties with defaults: 3+1 strategies

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:

Categories
Articles Original Tutorials

Inherit ancestor font-size, for fun and profit

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.

Categories
Articles Original Tutorials

Dark mode in 5 minutes, with inverted lightness variables

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.

Categories
Original Tips

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

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?

Categories
Apps & scripts Original

Parsel: A tiny, permissive CSS selector parser

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: https://projects.verou.me/parsel/

Categories
Apps & scripts Original

Releasing MaVoice: A free app to vote on repo issues

First off, some news: I agreed to be this year’s CSS content lead for the Web Almanac! One of the first things to do is to flesh out what statistics we should study to answer the question “What is the state of CSS in 2020?”. You can see last year’s chapter to get an idea of what kind of statistics could help answer that question.

Of course, my first thought was “We should involve the community! People might have great ideas of statistics we could study!”. But what should we use to vote on ideas and make them rise to the top?

Categories
Articles Original

The Cicada Principle, revisited with CSS variables

Many of today’s web crafters were not writing CSS at the time Alex Walker’s landmark article The Cicada Principle and Why it Matters to Web Designers was published in 2011. Last I heard of it was in 2016, when it was used in conjunction with blend modes to pseudo-randomize backgrounds even further.

So what is the Cicada Principle and how does it relate to web design in a nutshell? It boils down to: when using repeating elements (tiled backgrounds, different effects on multiple elements etc), using prime numbers for the size of the repeating unit maximizes the appearance of organic randomness. Note that this only works when the parameters you set are independent.

When I recently redesigned my blog, I ended up using a variation of the Cicada principle to pseudo-randomize the angles of code snippets. I didn’t think much of it until I saw this tweet:

Categories
Apps & scripts Articles CSS WG Original

LCH colors in CSS: what, why, and how?

I was always interested in color science. In 2014, I gave a talk about CSS Color 4 at various conferences around the world called “The Chroma Zone”. Even before that, in 2009, I wrote a color picker that used a hidden Java applet to support ICC color profiles to do CMYK properly, a first on the Web at the time (to my knowledge). I never released it, but it sparked this angry rant.

Color is also how I originally met my now husband, Chris Lilley: In my first CSS WG meeting in 2012, he approached me to ask a question about CSS and Greek, and once he introduced himself I said “You’re Chris Lilley, the color expert?!? I have questions for you!”. I later discovered that he had done even more cool things (he was a co-author of PNG and started SVG 🤯), but at the time, I only knew of him as “the W3C color expert”, that’s how much into color I was (I got my color questions answered much later, in 2015 that we actually got together).

My interest in color science was renewed in 2019, after I became co-editor of CSS Color 5, with the goal of fleshing out my color modification proposal, which aims to allow arbitrary tweaking of color channels to create color variations, and combine it with Una’s color modification proposal. LCH colors in CSS is something I’m very excited about, and I strongly believe designers would be outraged we don’t have them yet if they knew more about them.

Categories
Apps & scripts Articles

Refresh CSS Bookmarklet v2

Almost 11 years ago, Paul Irish posted this brilliant bookmarklet to refresh all stylesheets on the current page. Despite the amount of tools, plugins, servers to live reload that have been released over the years, I’ve always kept coming back to it. It’s incredibly elegant in its simplicity. It works everywhere: locally or remotely, on any domain and protocol. No need to set up anything, no need to alter my process in any way, no need to use a specific local server or tool. It quietly just accepts your preferences and workflow instead of trying to change them. Sure, it doesn’t automatically detect changes and reload, but in most cases, I don’t want it to.

I’ve been using this almost daily for a decade and there’s always been one thing that bothered me: It doesn’t work with iframes. If the stylesheet you’re editing is inside an iframe, tough luck. If you can open the frame in a new tab, that works, but often that’s nontrivial (e.g. the frame is dynamically generated). After dealing with this issue today once more, I thought “this is just a few lines of JS, why not fix it?”.

The first step was to get Paul’s code in a readable format, since the bookmarklet is heavily minified:

(function() {
	var links = document.getElementsByTagName('link');
	for (var i = 0; i < links.length; i++) {
		var link = links[i];
		if (link.rel.toLowerCase().match(/stylesheet/) && link.href) {
			var href = link.href.replace(/(&|%5C?)forceReload=\d+/, '');
			link.href = href + (href.match(/\?/) ? '&' : '?') + 'forceReload=' + (new Date().valueOf())
		}
	}
})()

Once I did that, it became obvious to me that this could be shortened a lot; the last 10 years have been wonderful for JS evolution!

(()=>{
	for (let link of Array.from(document.querySelectorAll("link[rel=stylesheet][href]"))) {
		var href = new URL(link.href, location);
		href.searchParams.set("forceReload", Date.now());
		link.href = href;
	}
})()

Sure, this reduces browser support a bit (most notably it excludes IE11), but since this is a local development tool, that’s not such a big problem.

Now, let’s extend this to support iframes as well:

{
	let $$ = (selector, root = document) => Array.from(root.querySelectorAll(selector));
	
	let refresh = (document) => {
		for (let link of $$("link[rel=stylesheet][href]", document)) {
			let href = new URL(link.href);
			href.searchParams.set("forceReload", Date.now());
			link.href = href;
		}

		for (let iframe of $$("iframe", document)) {
			iframe.contentDocument && refresh(iframe.contentDocument);
		}
	}

	refresh();
}

That’s it! Do keep in mind that this will not work with cross-origin iframes, but then again, you probably don’t expect it to in that case.

Now all we need to do to turn it into a bookmarklet is to prepend it with javascript: and minify the code. Here you go:

Refresh CSS

Hope this is useful to someone else as well 🙂
Any improvements are always welcome!

Credits

  • Paul Irish, for the original bookmarklet
  • Maurício Kishi, for making the iframe traversal recursive (comment)
Categories
Original Tips

Easy Dynamic Regular Expressions with Tagged Template Literals and Proxies

If you use regular expressions a lot, you probably also create them from existing strings that you first need to escape in case they contain special characters that need to be matched literally, like $ or +. Usually, a helper function is defined (hopefully this will soon change as RegExp.escape() is coming!) that basically looks like this:

var escapeRegExp = s => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");

and then regexps are created by escaping the static strings and concatenating them with the rest of the regex like this:

var regex = RegExp(escapeRegExp(start) + '([\\S\\s]+?)' + escapeRegExp(end), "gi")

or, with ES6 template literals, like this:

var regex = RegExp(`${escapeRegExp(start)}([\\S\\s]+?)${escapeRegExp(end)}`, "gi")

(In case you were wondering, this regex is taken directly from the Mavo source code)

Isn’t this horribly verbose? What if we could define a regex with just a template literal (`${start}([\\S\\s]+?)${end}` for the regex above) and it just worked? Well, it turns out we can! If you haven’t seen tagged template literals before, I suggest you click that MDN link and read up. Basically, you can prepend an ES6 template literal with a reference to a function and the function accepts the static parts of the string and the dynamic parts separately, allowing you to operate on them!