W3C home > Mailing lists > Public > www-style@w3.org > February 2015

CSS extensions / custom selectors

From: Matt Mastracci <matthew@mastracci.com>
Date: Wed, 4 Feb 2015 09:58:46 -0700
Message-Id: <67629EDE-6AFB-4B70-8986-2476983BC094@mastracci.com>
To: www-style list <www-style@w3.org>
Thinking more about the CSS extensions draft and custom selectors...

I think we need to be very careful about allowing any turing-complete language to have its hooks in CSS rules -- most of what the spec already shows can already be done using DOM mutation observers and temporary classes. If someone wants to prototype selectors in JS, I believe that it can be done through that channel.

I spent some time thinking/searching for some use cases for selectors not covered. My conclusion is that for these to be useful, the :has() selector *really* needs to be made fast, or included in the spec with the caveat that overuse will cause perf issues.

1) Selecting a label by input type


/* A selector that bolds the label for the associated focused input */
form input:focused /label-for/ label {
	font-weight: bold;

/* I’m inventing an “attribute capturing” := syntax here, bike-shedding welcome */

@custom-selector $a /--label-for/ $b
	$a[$id := “id”] /--root/ $b[for=$id];

@custom-selector $a /--root/ $b
	:root:has($a) $b;

Output equivalent:

form input:focused[$id := “id”] /--root/ label[for=$id]
:root:has(form input:focused[$id := “id”]) label[for=$id] { … }

2) Selecting a parent node of a subject

/* If a div has more than 10 siblings, show an overflow */
div.container:--n-children-of(10,.item) .overflow {
	display: block !important;

/* Shrink the font size when there are 5 or more items */
div.container .item:--n-siblings-of(5,.item) {
	font-size: smaller;

@custom-selector $subject:--n-siblings-of($n,$s)
	$subject:nth-child(1 of $s):nth-last-child($n of $s),
	$subject:nth-child(1 of $s):nth-last-child($n of $s) ~ $subject:nth-child(n of $s)

@custom-selector $subject:--n-children-of($n,$s)
	$subject:has(> :nth-child(1 of $s):nth-last-child($n of $s));

Output expansions:

div.container:--n-children-of(10,.item) .overflow
div.container:has(> :nth-child(1 of .item):nth-last-child(10 of .item)) .overflow { … }

div.container .item:--n-siblings-of(5,.item)
div.container .item:nth-child(1 of .item):nth-last-child(10 of .item):—self-of-sibling()
div.container :matches(.item:nth-child(1 of .item):nth-last-child(10 of .item), .item:nth-child(1 of .item):nth-last-child(10 of .item) ~ .item:nth-child(n of .item)) { … }

3) Previous/any sibling combinators

input /any-sibling/ label {
	float: left;

@custom-selector $a /--parent/ $b
	$b:has(> $a);

@custom-selector $a /--previous-sibling/ $b
	$a /--parent/ * > $b:has(+ $a);

@custom-selector $a /--any-sibling/ $b
	$a + $b, $a /--previous-sibling/ $b

Output expansion:

input + label, input /--previous-sibling/ label
input + label, input /--parent/ * > label:has(+ input)
input + label, *:has(> input) > label:has(+ input)


@custom-selector $a /--any-sibling/ $b
	$a /--parent/ * > $b

Output expansion:

*:has(input) > label

4) Self or sibling

/* Make all columns a checkmark until the end of the row */
.row .col:hover:—self-or-sibling-matching(.col) .check-img {
	background-image: url(checked.png)

@custom-selector $subject:-—self-or-sibling-matching($s)
	$subject, $subject ~ $s

Output expansion:

.row .col:hover:—self-or-sibling-matching(.col) .check-img
.row :matches(.col:hover, .col:hover ~ .col) .check-img

Received on Wednesday, 4 February 2015 16:59:18 UTC

This archive was generated by hypermail 2.4.0 : Friday, 25 March 2022 10:08:51 UTC