CSS Nesting and the Cascade | WebKit
As well as a very welcome announcement, Jen has a really good question for you about nesting in CSS.
If you have an opinion on the answer, please chime in.
As well as a very welcome announcement, Jen has a really good question for you about nesting in CSS.
If you have an opinion on the answer, please chime in.
A collection of stylesheets that don’t use class selectors. Think of them as alternatives to default user-agent stylesheets.
A terrific tour of just some of the fantastic ways you can use :has()
in CSS.
The section on using it with sibling selectors blew my mind:
How often have you wanted to adjust the margins on a headline based on the element following it? Now it’s easy. This code allows us to select any h2 with a p immediately after it.
h2:has(+ p) { margin-bottom: 0; }
Amazing.
I feel like I’m starting to understand how the CSS :where
pseudo-class works and why it’s useful. The cogs are slowly turning in my brain.
This detailed proposal from Miriam for scoping CSS is well worth reading—it makes a lot of sense to me.
This is supposed to be a defence of utility classes …but it’s actually a great explanation of why classes in general are a great mechanism for styling.
I don’t think anyone has ever seriously suggested using inline styles—the actual disagreement is about how ludicrously rigid and wasteful the class names dictated by something like Tailwind are. When people criticise those classes they aren’t advocating for inline styles—they’re advocating for better class names and making more use of the power of the class selector in CSS, not less.
Anyway, if you removed every instance of the word “utility” from this article, it would still work.
This would be such a great addition to CSS—a parent/ancestor selector!
With the combined might of :has()
, :not()
, nth-child()
, and calc()
, CSS has become a powerful language for specifying rules to account for all kinds of situations.
Oh, this is smart! You can’t target pseudo-elements in JavaScript, but you can use custom properties as a proxy instead.
If we, as a community, start to appreciate the complexity of writing CSS, perhaps we can ask for help instead of blaming the language when we’re confused or stuck. We might also stop looking down on CSS specialists.
A handy tool for getting an overview of your site’s CSS:
CSS Stats provides analytics and visualizations for your stylesheets. This information can be used to improve consistency in your design, track performance of your app, and diagnose complex areas before it snowballs out of control.
I wrote a little something recently about using ARIA attributes as selectors in CSS. For me, one of the advantages is that because ARIA attributes are generally added via JavaScript, the corresponding CSS rules won’t kick in if something goes wrong with the JavaScript:
Generally, ARIA attributes—like
aria-hidden
—are added by JavaScript at runtime (rather than being hard-coded in the HTML).
But there’s one instance where I actually put the ARIA attribute directly in the HTML that gets sent from the server: aria-live
.
If you’re not familiar with it, aria-live
is extremely useful if you’ve got any dynamic updates on your page—via Ajax, for example. Let’s say you’ve got a bit of your site where filtered results will show up. Slap an aria-live
attribute on there with a value of “polite”:
<div aria-live="polite">
...dynamic content gets inserted here
</div>
You could instead provide a value of “assertive”, but you almost certainly don’t want to do that—it can be quite rude.
Anyway, on the face it, this looks like exactly the kind of ARIA attribute that should be added with JavaScript. After all, if there’s no JavaScript, there’ll be no dynamic updates.
But I picked up a handy lesson from Ire’s excellent post on using aria-live
:
Assistive technology will initially scan the document for instances of the aria-live attribute and keep track of elements that include it. This means that, if we want to notify users of a change within an element, we need to include the attribute in the original markup.
Good to know!
Sara tweeted something recently that resonated with me:
Also, Pro Tip: Using ARIA attributes as CSS hooks ensures your component will only look (and/or function) properly if said attributes are used in the HTML, which, in turn, ensures that they will always be added (otherwise, the component will obv. be broken)
Yes! I didn’t mention it when I wrote about accessible interactions but this is my preferred way of hooking up CSS and JavaScript interactions. Here’s old Codepen where you can see it in action:
[aria-hidden='true'] {
display: none;
}
In order for the functionality to work for everyone—screen reader users or not—I have to make sure that I’m toggling the value of aria-hidden
in my JavaScript.
There’s another advantage to this technique. Generally, ARIA attributes—like aria-hidden
—are added by JavaScript at runtime (rather than being hard-coded in the HTML). If something goes wrong with the JavaScript, the aria-hidden
value isn’t set to “true”, which means that the CSS never kicks in. So the default state is for content to be displayed. There’s no assumption that the JavaScript has to work in order for the CSS to make sense.
It’s almost as though accessibility and progressive enhancement are connected somehow…
An excellent and clear explanation of specificity in CSS.
I can see this coming in very handy at Codebar—pop any CSS selector in here and get a plain English explanation of what it’s doing.
This is a wonderful interactive explanation of the way CSS hierarchy works—beautiful!
Everyone wants it, but it sure seems like no one is actively working on it.
Zach traces the earliest inklings of container queries to an old blog post of Andy’s—back when he was at Clearleft—called Responsive Containers:
For fun, here’s some made-up syntax (which Jeremy has dubbed ‘selector queries’)…
Well, the clever CSS techniques just keep on comin’ from Trys—I’m learning so much from him!
We have a saying at Clearleft:
Everything is a tiny lesson.
I bet you learn something new every day, even if it’s something small. These small tips and techniques can easily get lost. They seem almost not worth sharing. But it’s the small stuff that takes the least effort to share, and often provides the most reward for someone else out there. Take for example, this great tip for getting assets out of Sketch that Cassie shared with me.
Cassie was working on a piece of JavaScript yesterday when we spotted a tiny lesson that tripped up both of us. The script was a fairly straightforward piece of DOM scripting. As a general rule, we do a sort of feature detection near the start of the script. Let’s say you’re using querySelector
to get a reference to an element in the DOM:
var someElement = document.querySelector('.someClass');
Before going any further, check to make sure that the reference isn’t falsey (in other words, make sure that DOM node actually exists):
if (!someElement) return;
That will exit the script if there’s no element with a class of someClass
on the page.
The situation that tripped us up was like this:
var myLinks = document.querySelectorAll('a.someClass');
if (!myLinks) return;
That should exit the script if there are no A
elements with a class of someClass
, right?
As it turns out, querySelectorAll
is subtly different to querySelector
. If you give querySelector
a reference to non-existent element, it will return a value of null
(I think). But querySelectorAll
always returns an array (well, technically it’s a NodeList but same difference mostly). So if the selector you pass to querySelectorAll
doesn’t match anything, it still returns an array, but the array is empty. That means instead of just testing for its existence, you need to test that it’s not empty by checking its length
property:
if (!myLinks.length) return;
That’s a tiny lesson.
This is a great explanation of the difference between the [lang]
and :lang
CSS selectors. I wouldn’t even have thought’ve the differences so this is really valuable to me.
There’s a worrying tendency for “real” programmers look down their noses at CSS. It’s just a declarative language, they point out, not a fully-featured programming language. Heck, it isn’t even a scripting language.
That may be true, but that doesn’t mean that CSS isn’t powerful. It’s just powerful in different ways to traditional languages.
Take CSS selectors, for example. At the most basic level, they work like conditional statments. Here’s a standard if
statement:
if (condition) {
// code here
}
The condition needs to evaluate to true
in order for the code in the curly braces to be executed. Sound familiar?
condition {
// styles here
}
That’s a very simple mapping, but what if the conditional statement is more complicated?
if (condition1 && condition2) {
// code here
}
Well, that’s what the decendant selector does:
condition1 condition2 {
// styles here
}
In fact, we can get even more specific than that by using the child combinator, the sibling combinator, and the adjacent sibling combinator:
condition1 > condition2
condition1 ~ condition2
condition2 + condition2
AND is just one part of Boolean logic. There’s also OR:
if (condition1 || condition2) {
// code here
}
In CSS, we use commas:
condition1, condition2 {
// styles here
}
We’ve even got the :not()
pseudo-class to complete the set of Boolean possibilities. Once you add quantity queries into the mix, made possible by :nth-child
and its ilk, CSS starts to look Turing complete. I’ve seen people build state machines using the adjacent sibling combinator and the :checked
pseudo-class.
Anyway, my point here is that CSS selectors are really powerful. And yet, quite often we deliberately choose not to use that power. The entire raison d’être for OOCSS, BEM, and Smacss is to deliberately limit the power of selectors, restricting them to class selectors only.
On the face of it, this might seem like an odd choice. After all, we wouldn’t deliberately limit ourselves to a subset of a programming language, would we?
We would and we do. That’s what templating languages are for. Whether it’s PHP’s Smarty or Twig, or JavaScript’s Mustache, Nunjucks, or Handlebars, they all work by providing a deliberately small subset of features. Some pride themselves on being logic-less. If you find yourself trying to do something that the templating language doesn’t provide, that’s a good sign that you shouldn’t be trying to do it in the template at all; it should be in the controller.
So templating languages exist to enforce simplicity and ensure that the complexity happens somewhere else. It’s a similar story with BEM et al. If you find you can’t select something in the CSS, that’s a sign that you probably need to add another class name to the HTML. The complexity is confined to the markup in order to keep the CSS more straightforward, modular, and maintainable.
But let’s not forget that that’s a choice. It’s not that CSS in inherently incapable of executing complex conditions. Quite the opposite. It’s precisely because CSS selectors (and the cascade) are so powerful that we choose to put guard rails in place.