You make a mental note to use a[href^=https://specific-domain.com] the next time you want to use pink underlines for each link to one specific domain, and promptly forget about it forever. (Anyone use this one? …Ever?)
I use a[href^="https://"] for styling external links.
That’s a great example. I wanted to highlight how attribute selectors have often been suggested for very specific, sometimes weird features, but of course for every example there is always that one perfectly valid and pragmatic use case like this 😛
Because the latter increases specificity, making it harder to override, which leads to “specificity wars” as developers have to keep upping the specificity of their styles to override styles written by the last developer.
Specifically, we should be using a lot less classes as we can infer a lot of our selection based on context. (i.e. main > article p {})
So, making evident that the CSS and HTML are very coupled.
At that point you should realize BEM is too much work and it would be better to use TailwindCSS
Instead of <article class="card" data-align="left" data-size="small" />
you will write <article class="card text-left w-1/2" />
So, making evident that the CSS and HTML are very coupled.
It’s not coupled. This selector need not match every page, or even any page. You write CSS that semantically marks up “the main article’s paragraph tags”, and if those exist, they get styled. If they don’t, they don’t.
One can have a general stylesheet applicable to many documents, and many different types of documents, and different structures of documents.
The stylesheet my blogs have used (in the past) were also usable with my articles extracted outside the blog template, or to printable form, or to emails. Not all styles are applied at all times. Only when they need to be, in the specific way they need to be. And in the pieces they need to be, cascading down.
Tailwind, on the other hand, is the exact opposite of CSS, and fails to separate presentation from data. You may as well just write inline style attributes instead of use Tailwind.
Why not? If we do what @boh suggested then the developer can see exactly how it’s styled right there where the article tag is created. Otherwise you have to go find the CSS that targets that tag (not always simple!) and then make the change there, hoping it doesn’t also affect something else. Unless you target by a unique classname or ID, which is still coupling your markup to your presentation, just with an annoying layer of indirection.
I was around in the HTML 1.0 days. This is totally different. That was all table soup and image maps. Today we have a combination of semantic tags (articlesectionnav etc ) and attributes (aria-whatever) plus semantically inert placeholders (divspan). There’s no real reason to avoid the semantically inert placeholders. It’s not like you’re making a table and then using the table heading to hold the navbar anymore. It was bad because we had to use table for things that weren’t tables, not because everything needs semantics.
Making your markup independent of style makes it easier to change the style, both for you and for your users. And conversely, making stylesheets independent of markup makes them reusable.
My understanding – please correct me if I’m wrong – is that this approach only works if you move the responsibility for defining “card” to a React component (or something similar), and so “re-use of the card concept” becomes “rendering the react card component” rather than “applying the card class”.
That is, from a first principles perspective, you still want the concept of “card” as reusable thing to exist… it’s just an argument about what method we use to implement?
Yeah, although it varies. Sometimes it’s good just to copy and paste because then you aren’t DRYing things together that don’t actually need to be DRYed together, since the visual similarity is just coincidental.
@nolan posted an article a couple months ago benchmarking different kinds of selectors. Attribute selectors outside a shadow DOM context were many times slower than classes.
Depends on how they’re used. People routinely query css properties in loops that also mutate css properties. Browser engines do a lot of work to avoid recomputing layout but it’s very easy to do something where a browser can’t avoid it, and suddenly perf becomes important again.
People routinely query css properties in loops that also mutate css properties.
I mean, they did in jQuery days, but none of the modern frameworks encourage that kind of behavior. Instead you have state in JS land that drives the CSS properties, but no backflow from CSS to JS if you can help it. For things where you really need backflow, they have special helpers like IntersectionObserver and ResizeObserver.
People do this today, and it isn’t tied to jquery, or the jquery era.
The big modern frameworks are horrific because their idea of helping performance means they do a lot of things to undermine the browser’s layout engine, despite the browser engine being much better, faster, and with less memory usage than the many and varied “lets implement our own layout system on top of the real layout system” frameworks.
Sorry for the late response, lobster’s notifications seem super wack for me and sometimes say there are responses immediately, and sometime (as here) a week later???
Yeah, Lobsters doesn’t seem to update its new message queue with any regularity. I don’t understand it.
On the topic of performance, going back to jQuery it was known that querying a CSS property like size in a loop and also modifying it caused layout thrash that killed performance and you needed to batch the reads and writes to get decent performance. The thing with React et al is if you follow their happy path, you won’t run into that problem. I agree that React introduces its own problems—VDOM seems like a mistaken optimization to me—but even in vanilla JS, you have to be aware of the need to batch to get good performance. Fortunately a lot of animations can just be done with pure CSS these days, so there’s less need to use JS for anything more than just toggling a class.
The article I linked to answers that question with bar charts and methodology. In short, depending on the hardware, browser, and the scale of the app, it could be a difference of 500ms or more in applying styling. It’d be a lot smaller if you know for sure the styling and the complexity of the elements are both going to stay fairly small. But using attribute selectors as a default for everyone? Seems like a performance footgun.
I’d say it depends. :) Also the perf has changed recently after WebKit and Firefox made some optimizations to attribute selectors.
In general I’d say my data mostly applies to cases where a selector pattern is repeated multiple times, e.g. a component framework or design system. For one-off selectors, it’s probably not worth worrying about attributes vs classes. And I agree with the author on the benefits for a11y of tying your selectors directly to the semantics of the element.
I actually have a talk next week at performance.now() where I’ll go into some of the nuance of this; the video should be available afterward! :)
I use
a[href^="https://"]
for styling external links.That’s a great example. I wanted to highlight how attribute selectors have often been suggested for very specific, sometimes weird features, but of course for every example there is always that one perfectly valid and pragmatic use case like this 😛
There are a lot more methods of selection than just classes and IDs, and I find it incredible we don’t use more of them.
Specifically, we should be using a lot less classes as we can infer a lot of our selection based on context. (i.e.
main > article p {}
)Could someone explain why, according to the BEM philosophy:
is better than:
Genuinely just want to understand the perspective.
Because the latter increases specificity, making it harder to override, which leads to “specificity wars” as developers have to keep upping the specificity of their styles to override styles written by the last developer.
Thanks, it looks like they have an FAQ for this question which gives some more detailed examples:
Why BEM modifer classes are prefixed
So, making evident that the CSS and HTML are very coupled. At that point you should realize BEM is too much work and it would be better to use TailwindCSS
Instead of
<article class="card" data-align="left" data-size="small" />
you will write
<article class="card text-left w-1/2" />
It’s not coupled. This selector need not match every page, or even any page. You write CSS that semantically marks up “the main article’s paragraph tags”, and if those exist, they get styled. If they don’t, they don’t.
One can have a general stylesheet applicable to many documents, and many different types of documents, and different structures of documents.
The stylesheet my blogs have used (in the past) were also usable with my articles extracted outside the blog template, or to printable form, or to emails. Not all styles are applied at all times. Only when they need to be, in the specific way they need to be. And in the pieces they need to be, cascading down.
Tailwind, on the other hand, is the exact opposite of CSS, and fails to separate presentation from data. You may as well just write inline style attributes instead of use Tailwind.
Don’t put presentation in your markup when not necessary. Just
<article>
will do, then you stylearticle
elements.Why not? If we do what @boh suggested then the developer can see exactly how it’s styled right there where the
article
tag is created. Otherwise you have to go find the CSS that targets that tag (not always simple!) and then make the change there, hoping it doesn’t also affect something else. Unless you target by a unique classname or ID, which is still coupling your markup to your presentation, just with an annoying layer of indirection.This is literally the exact opposite of what CSS was created for, and what we did in the HTML 1.0 days.
I was around in the HTML 1.0 days. This is totally different. That was all table soup and image maps. Today we have a combination of semantic tags (
article
section
nav
etc ) and attributes (aria-whatever
) plus semantically inert placeholders (div
span
). There’s no real reason to avoid the semantically inert placeholders. It’s not like you’re making a table and then using the table heading to hold the navbar anymore. It was bad because we had to use table for things that weren’t tables, not because everything needs semantics.Many things about the web have changed since CSS was created 25 years ago, I’m not that it’s intended purpose at that time is necessarily relevant.
Which is precisely what you don’t want. Styling independent of markup.
I’ve said a reason why I do want that, could you explain why you don’t?
Making your markup independent of style makes it easier to change the style, both for you and for your users. And conversely, making stylesheets independent of markup makes them reusable.
I am a Tailwind partisan, so I will criticize you from the other side, that “card” should just be
shadow p-2 rounded border-2 border-gray-400
etc. :-)My understanding – please correct me if I’m wrong – is that this approach only works if you move the responsibility for defining “card” to a React component (or something similar), and so “re-use of the card concept” becomes “rendering the react card component” rather than “applying the card class”.
That is, from a first principles perspective, you still want the concept of “card” as reusable thing to exist… it’s just an argument about what method we use to implement?
Yeah, although it varies. Sometimes it’s good just to copy and paste because then you aren’t DRYing things together that don’t actually need to be DRYed together, since the visual similarity is just coincidental.
@nolan posted an article a couple months ago benchmarking different kinds of selectors. Attribute selectors outside a shadow DOM context were many times slower than classes.
sure, but how relevant is that additional effort in page rendering?
Depends on how they’re used. People routinely query css properties in loops that also mutate css properties. Browser engines do a lot of work to avoid recomputing layout but it’s very easy to do something where a browser can’t avoid it, and suddenly perf becomes important again.
I mean, they did in jQuery days, but none of the modern frameworks encourage that kind of behavior. Instead you have state in JS land that drives the CSS properties, but no backflow from CSS to JS if you can help it. For things where you really need backflow, they have special helpers like IntersectionObserver and ResizeObserver.
No.
People do this today, and it isn’t tied to jquery, or the jquery era.
The big modern frameworks are horrific because their idea of helping performance means they do a lot of things to undermine the browser’s layout engine, despite the browser engine being much better, faster, and with less memory usage than the many and varied “lets implement our own layout system on top of the real layout system” frameworks.
Sorry for the late response, lobster’s notifications seem super wack for me and sometimes say there are responses immediately, and sometime (as here) a week later???
Yeah, Lobsters doesn’t seem to update its new message queue with any regularity. I don’t understand it.
On the topic of performance, going back to jQuery it was known that querying a CSS property like size in a loop and also modifying it caused layout thrash that killed performance and you needed to batch the reads and writes to get decent performance. The thing with React et al is if you follow their happy path, you won’t run into that problem. I agree that React introduces its own problems—VDOM seems like a mistaken optimization to me—but even in vanilla JS, you have to be aware of the need to batch to get good performance. Fortunately a lot of animations can just be done with pure CSS these days, so there’s less need to use JS for anything more than just toggling a class.
The article I linked to answers that question with bar charts and methodology. In short, depending on the hardware, browser, and the scale of the app, it could be a difference of 500ms or more in applying styling. It’d be a lot smaller if you know for sure the styling and the complexity of the elements are both going to stay fairly small. But using attribute selectors as a default for everyone? Seems like a performance footgun.
I’d say it depends. :) Also the perf has changed recently after WebKit and Firefox made some optimizations to attribute selectors.
In general I’d say my data mostly applies to cases where a selector pattern is repeated multiple times, e.g. a component framework or design system. For one-off selectors, it’s probably not worth worrying about attributes vs classes. And I agree with the author on the benefits for a11y of tying your selectors directly to the semantics of the element.
I actually have a talk next week at performance.now() where I’ll go into some of the nuance of this; the video should be available afterward! :)
Looking forward to it! Thanks for chiming in.
Looking forward to doing this once has is in firefox
. field:has(input[required]) label:after { content: “*”; }
I use
a[href^='https://twitter.com/']::before { content: "🐦"; }
etc. on a site allowing css injection: https://blog.fefe.de/?ts=9da6f548&css=https://mro.name/fefe.css