I :has() an idea about lists
Regrets, dear reader, but this new CSS feature required an ancient meme reference.
This was originally published on .
While I was rewriting this site's CSS, I wanted to find something that could benefit from the new :has()
pseudo-class, without inventing a need for it.
Friend, that something is lists.
I love lists. Ordered, unordered, definition, I don't care, you have a set of things, you need a list. One thing that's bothered me about lists--specifically of the un/ordered variety, is that they can become pretty long and scroll-y.
And with :has()
, we can wrangle those lengthy lists pretty easily.
.smarter-list {
columns: var(--listColumns);
}
.smarter-list:has(:nth-child(n+21)) {
--listColumns: 2;
}
.smarter-list:has(:nth-child(n+41)) {
--listColumns: 3;
}
What's going on here? First, any .smarter-list
should use columns for its layout. Great, that's reasonable, but how many? By default, none, as --listColumns
is undefined without a default, and the browser moves on with its life. But if the .smarter-list
has twenty-one child elements, it should use a two-column layout. Why twenty-one? Because I arbitrarily chose twenty as my per-column limit, so the presence of a twenty-first child covers children 21-40. And for more than forty? Double the limit, add one, and boom, three columns.
Here's a CodePen if that's helpful.
I think this pattern is helpful because it keeps the number of classes in my markup to a minimum, and it doesn't require processing in a template to determine which classes to apply based on how many items are in a given list. For this site specifically, I'd have to either do a RegEx test on the raw Markdown in my posts, or do a similar test on the compiled HTML. Put simply, it's overhead I don't want to deal with.
Now, :has()
does not have universal support yet, so what's a front-end maker like yourself supposed to do about that? CSS Grid can do the same thing, but with slightly more complex selectors.
.smarter-list {
display: grid;
grid-template-columns: repeat(3, auto);
grid-auto-flow: column;
}
.smarter-list :nth-child(n+1) ~ * {
grid-column: 1;
}
.smarter-list :nth-child(n+20) ~ * {
grid-column: 2;
}
.smarter-list :nth-child(n+40) ~ * {
grid-column: 3;
}
This works much the same way as the :has()
method, except that the column assignment is happening "manually" on the sibling elements of the twentieth and fortieth .smarter-list
children. This boils down to the difference between CSS Grid and CSS Columns--along with Floats and Flexbox, a selection of layout tools in our kits, equal in their value and beauty.
Here's another CodePen in these trying times.
For me and my strong opinions tightly held, I prefer to use single-class selectors to keep specificity low, and by my math, both methods here result in the same specificity (20), but using :has()
also lets me keep nesting out of my selectors.
My ideal CSS selector is a single class, with psuedo-classes/elements added as necessary. I like to avoid nesting in general, because I like to keep my specificity as low as possible. By my math, both .smarter-lists
methods result in the same specificity (20). I think I prefer the :has()
method as it lets me live my unnested fantasy, and ultimately require less code. But also? C'mon, it's the new and shiny!