It’s amazing that CSS has such advanced features that can do these things, but at the same time if you actually use them extensively and then I inherit your code, you kind of deserve a knifing.
Well at least you can google it… But it certainly doesn’t help. I keep my css specific to each class and try to be as explicit as possible to avoid any weird consequences.
That’s kind of the opposite problem that the article mentions though — if every component has its own specific CSS, then you don’t really have CSS, you just have SS. You miss out on the cascading.
I develop things at work similarly to you. JSS where every component has its own style, but you have a common `theme` file so that you can reuse the common styles via a library.
But I have a certain romantic feeling about… what if we could do 90%+ of our styling using these global-per-page rules, and overriding when necessary. The simplicity is really appealing
For me, your approach makes the CSS harder to maintain. Pages have loads of repetition and patterns. I’d hope to find a good balance between DRYing up patterns that benefit from it, and leaving space for special cases. I find code patterns like the owl/.flow shown in the OP help greatly with that.
Your approach, redefining all the properties per class, echoes avoiding using parent+subclass relationships in OO style code. Sometimes you benefit from the generalisation of a class hierarchy, sometimes you don’t. A hard rule in either direction gets in everyone's way.
The 'C' in CSS was a mistake. Consider that specificity is even a thing, and then consider that the ordering of your declarative CSS code matters. IDK, it does seem necessary to make the language workable, but there are far too many stylesheets out there with specificity hacks and !important.
It's almost like you're saying: because people use CSS without learning it or the problem domain it tries to solve, CSS is wrong.
The "C" is an attempt to make it easy to have a mixture of generic rules that apply across the whole page/site, and specific rules for alternatives and edge cases. CSS and particularly the C make it really easy to say:
What's wrong with that? How would you do it otherwise? In a way that generalises to any HTML or XML element? Without changing HTML or XML? It's essentially a syntactic formulation of multiple inheritance. I accept that multiple inheritance is frowned upon and difficult, but it's not without precedent or practicality.
In response to "How would you do it otherwise?", like I said, it kind of seems like the language is just what it has to be in order to be workable. In other words, it seems like it designed itself to some extent. And you're right, CSS has practicality. But with almost any pre-built solution, those selectors would look something like `button.cta.xl-2-large:not(:is(.container > .button))`. At that level of complexity, which is not uncommon, you're better off just coming up with a unique ID for every situation and styling things by ID rather than trying to make them "semantic" with the selectors. That's the other problem is the mixture of selectors trying to be both semantic but also their increased expressivity changing the specificity. So if you change a selector, you might need to reorder your CSS or change a bunch of other selectors to match, which becomes untenable the more CSS you have.
I detect a pungent "code smell", and probably a design bug via your example selector.
Does the designer really want such a specific edge case to an edge case? Can they explain or justify it according to the design patterns they laid down? Why are we using such a general ".container" to specify a ".button", and whilst we're at it why do we have a "button" AND a ".button". Our codebase was going off the rails long before we reached this (admittedly scary-looking) selector. There was a better way to do what we're doing, several iterations ago, and people who cared about the ux+information design and the CSS should have caught it sooner. Go back to them and fix the problem there rather than let this horrible selector pass code review.
We wouldn't stand for this in the TS/JS codebase. Why do we put up with it in the CSS?
To be honest, I was never on board with the "classes everywhere" approach. IDs are fine when they identify a single item on the page. We lost something when we stopped using the language as it was designed.
I think the issue is that CSS is somewhere between unopinionated and encouraging complexity. Is that the best kind of selector? Certainly not, but in Bootstrap, one of the most widely-used CSS packages, it was trivial to find a similar one: `.table-striped-columns > :not(caption) > tr > :nth-child(even) `, so it definitely happens a lot.
Also a lot of it comes down to the original plan to have semantic HTML, which ultimately failed. But we rarely talk about whether semantic CSS is possible, I think the assumption is that it is. But IMO it's not practical because of the poor specificity controls. That's something I'd do different, add in a manual specificity level (like how z-index has manual weights).
I don't see so many problems with that Bootstrap selector. It's long, but right from the first class to the final element selector it's fairly obvious why that clause exists. In fact, the left-most one explains all the rest: from reading it, I can tell what it does and _why_ it's that long. Your earlier example, much less so.
the original plan to have semantic HTML, which ultimately failed
I feel that browser developers still build browsers with this in mind. You can see this expressed in the default ARIA roles, for example. I feel that developers of large web apps are the ones who aren't thinking about the semantics the right way and who think semantic HTML has failed. It didn't, they gave up trying to use it. Most of us think only in terms of using JS to render anonymous boxes, rather than about outputting an implementation of an underlying information design. Browsers and HTML still supports that very well. We're wrapping all the conceptual design up into JS and components, and treating the HTML + CSS as some kind of dumb output... when in fact that's the browser's job, and it hides that dumb render from us on purpose. If you use your web framework to output an expressive information design, the browser will render it just fine and more of your code will make sense (IMO).
I don't think you gain much from allowing manual specificity. No-one finds managing z-indexes easy. In fact, I think manual specificity would accentuate the problems we already have. You can fix most z-index issues by not using it, because the stacking is mostly handled already for you by the browser. Imagine trying to find the "specificity 203" in your codebase, or to establish a pattern for which disparate Thing A on the page is the same specificity as a Thing B. It'd be harder than the current system, I"m sure.
Bad code is innovative for no reason. Complex for no good reason. Hard to reason about (what does foo > * + * mean? and what happens when the writing is Chinese, exactly?).
These various CSS states would be much better expressed as named properties of React components. That way there would be explicit branches instead of allowing the CSS selector engine to do some implicit insanity.
To be fair, this is the first time I see this but I immediately got it. So might not be as hard / innovative for no reason. I believe someone working often with CSS should be able to parse *+*, and if not, take the time it takes once to understand it. It's nothing that implicit.
Anyway please please don't pull off a React to avoid CSS, just comment your CSS instead, or find a clearer way to write it if you prefer (:not(:first-child)).
This styles documents (puts margins on paragraphs), we don't want an app for this. And even for a React app, we have a nice language to style stuff at our disposal that our browsers are incredibly efficient at evaluating, I don't think reinventing CSS in React is a good thing.
Please do. I'd much rather see a system built in React with explicit logic, math, and names, than an implicit pile of obfuscated rules that are impossible to tweak without causing a cascading failure.
I was more referring to margin-block-start, var (with two arguments), gap... Also, no, sibling selector wasn't really supported very well until 2012, so only in the last ~30% of CSS's lifetime.
Comments
It’s amazing that CSS has such advanced features that can do these things, but at the same time if you actually use them extensively and then I inherit your code, you kind of deserve a knifing.
Worst thing is the comment (if there's any) is probably something like
Well at least you can google it… But it certainly doesn’t help. I keep my css specific to each class and try to be as explicit as possible to avoid any weird consequences.
That’s kind of the opposite problem that the article mentions though — if every component has its own specific CSS, then you don’t really have CSS, you just have SS. You miss out on the cascading.
I develop things at work similarly to you. JSS where every component has its own style, but you have a common `theme` file so that you can reuse the common styles via a library.
But I have a certain romantic feeling about… what if we could do 90%+ of our styling using these global-per-page rules, and overriding when necessary. The simplicity is really appealing
For me, your approach makes the CSS harder to maintain. Pages have loads of repetition and patterns. I’d hope to find a good balance between DRYing up patterns that benefit from it, and leaving space for special cases. I find code patterns like the owl/.flow shown in the OP help greatly with that.
Your approach, redefining all the properties per class, echoes avoiding using parent+subclass relationships in OO style code. Sometimes you benefit from the generalisation of a class hierarchy, sometimes you don’t. A hard rule in either direction gets in everyone's way.
The 'C' in CSS was a mistake. Consider that specificity is even a thing, and then consider that the ordering of your declarative CSS code matters. IDK, it does seem necessary to make the language workable, but there are far too many stylesheets out there with specificity hacks and !important.
It's almost like you're saying: because people use CSS without learning it or the problem domain it tries to solve, CSS is wrong.
The "C" is an attempt to make it easy to have a mixture of generic rules that apply across the whole page/site, and specific rules for alternatives and edge cases. CSS and particularly the C make it really easy to say:
button { border-radius: 20%; background-colour: teal; color: white; }
button.cta { background-colour: blue; colour: yellow; }
What's wrong with that? How would you do it otherwise? In a way that generalises to any HTML or XML element? Without changing HTML or XML? It's essentially a syntactic formulation of multiple inheritance. I accept that multiple inheritance is frowned upon and difficult, but it's not without precedent or practicality.
In response to "How would you do it otherwise?", like I said, it kind of seems like the language is just what it has to be in order to be workable. In other words, it seems like it designed itself to some extent. And you're right, CSS has practicality. But with almost any pre-built solution, those selectors would look something like `button.cta.xl-2-large:not(:is(.container > .button))`. At that level of complexity, which is not uncommon, you're better off just coming up with a unique ID for every situation and styling things by ID rather than trying to make them "semantic" with the selectors. That's the other problem is the mixture of selectors trying to be both semantic but also their increased expressivity changing the specificity. So if you change a selector, you might need to reorder your CSS or change a bunch of other selectors to match, which becomes untenable the more CSS you have.
I detect a pungent "code smell", and probably a design bug via your example selector.
Does the designer really want such a specific edge case to an edge case? Can they explain or justify it according to the design patterns they laid down? Why are we using such a general ".container" to specify a ".button", and whilst we're at it why do we have a "button" AND a ".button". Our codebase was going off the rails long before we reached this (admittedly scary-looking) selector. There was a better way to do what we're doing, several iterations ago, and people who cared about the ux+information design and the CSS should have caught it sooner. Go back to them and fix the problem there rather than let this horrible selector pass code review.
We wouldn't stand for this in the TS/JS codebase. Why do we put up with it in the CSS?
To be honest, I was never on board with the "classes everywhere" approach. IDs are fine when they identify a single item on the page. We lost something when we stopped using the language as it was designed.
I think the issue is that CSS is somewhere between unopinionated and encouraging complexity. Is that the best kind of selector? Certainly not, but in Bootstrap, one of the most widely-used CSS packages, it was trivial to find a similar one: `.table-striped-columns > :not(caption) > tr > :nth-child(even) `, so it definitely happens a lot.
Also a lot of it comes down to the original plan to have semantic HTML, which ultimately failed. But we rarely talk about whether semantic CSS is possible, I think the assumption is that it is. But IMO it's not practical because of the poor specificity controls. That's something I'd do different, add in a manual specificity level (like how z-index has manual weights).
I don't see so many problems with that Bootstrap selector. It's long, but right from the first class to the final element selector it's fairly obvious why that clause exists. In fact, the left-most one explains all the rest: from reading it, I can tell what it does and _why_ it's that long. Your earlier example, much less so.
I feel that browser developers still build browsers with this in mind. You can see this expressed in the default ARIA roles, for example. I feel that developers of large web apps are the ones who aren't thinking about the semantics the right way and who think semantic HTML has failed. It didn't, they gave up trying to use it. Most of us think only in terms of using JS to render anonymous boxes, rather than about outputting an implementation of an underlying information design. Browsers and HTML still supports that very well. We're wrapping all the conceptual design up into JS and components, and treating the HTML + CSS as some kind of dumb output... when in fact that's the browser's job, and it hides that dumb render from us on purpose. If you use your web framework to output an expressive information design, the browser will render it just fine and more of your code will make sense (IMO).
I don't think you gain much from allowing manual specificity. No-one finds managing z-indexes easy. In fact, I think manual specificity would accentuate the problems we already have. You can fix most z-index issues by not using it, because the stacking is mostly handled already for you by the browser. Imagine trying to find the "specificity 203" in your codebase, or to establish a pattern for which disparate Thing A on the page is the same specificity as a Thing B. It'd be harder than the current system, I"m sure.
Any developer describing this code as bad code has probably never seen bad code.
Bad code is innovative for no reason. Complex for no good reason. Hard to reason about (what does foo > * + * mean? and what happens when the writing is Chinese, exactly?).
These various CSS states would be much better expressed as named properties of React components. That way there would be explicit branches instead of allowing the CSS selector engine to do some implicit insanity.
To be fair, this is the first time I see this but I immediately got it. So might not be as hard / innovative for no reason. I believe someone working often with CSS should be able to parse *+*, and if not, take the time it takes once to understand it. It's nothing that implicit.
Anyway please please don't pull off a React to avoid CSS, just comment your CSS instead, or find a clearer way to write it if you prefer (:not(:first-child)).
This styles documents (puts margins on paragraphs), we don't want an app for this. And even for a React app, we have a nice language to style stuff at our disposal that our browsers are incredibly efficient at evaluating, I don't think reinventing CSS in React is a good thing.
React will die before CSS anyway.
Please do. I'd much rather see a system built in React with explicit logic, math, and names, than an implicit pile of obfuscated rules that are impossible to tweak without causing a cascading failure.
Maybe you wouldn't if you had to pay for all the electricity that's wasted by using React.
Something tells me React uses less electricity than CSS `*` selectors.
How is ".stack > * + *" advanced? This is CSS 101.
I was more referring to margin-block-start, var (with two arguments), gap... Also, no, sibling selector wasn't really supported very well until 2012, so only in the last ~30% of CSS's lifetime.