I work on systems software - kernel, daemons, networking, debuggers, machine language disassembly - so I'm used to looking at things which other people say are "hard".
Nothing frustrates me more than writing a Stylus rule or figuring out how to place an element on my Jekyll theme. Every time I do this, my appreciation for modern well-made websites doubles.
CSS is so opaque and impossible, I wouldn't be a web developer for any money.
In CSS you can't customize that latter half though, if the 3 things render wonky when they don't fit you just have to hope there is some way to tell CSS to render it the way you want. Sure, you can run a test that says it failed to create the layout you were hoping for... but you already knew that and test or no test your only hope is you can find some way to do it with the predefined options in CSS. And the fun part is sometimes there is no way! You may just have to compromise on how your layout works or push some logic to JS to change the styling so you can do something more akin to the normal code way (at the loss of performance and clarity).
The problem is most devs, even on the frontend, learn CSS incidentally, not from first principles, so they get annoyed when they can't do stuff. I mean, yeah, if you don't learn something properly of course it won't work as you expect. It's the same as me not understanding Docker until I also learned it from the ground up.
But I can't point any functionality that could go through that. Ok, there are a few functionalities that are not independent, but they are still better designed than any alternative I can think.
I'm staying with the opinion that a universal formatting language is inherently complex. And CSS is quite well fit to function.
The only things missing from it having universal logic are coming with the ":has" selector. So there isn't anything to argue about performance. The new ":has" is a performance killer, everybody knows it, nobody really cares because it's only a problem if you abuse it... and of course people will make a point of abusing it.
Interestingly, it is also getting predicate composition, with hierarchical selectors. Looks like the CSS people finally decided to turn it into a usable logic language. The auto generated sites have huge amounts of them because both auto generated code tends to be bad, and because it lacks composition. One of those is being solved.
I'm not familiar with what you're referring to with hierarchical selectors. CSS combinators have always let you select different types of decedents and :has() lets you select either way. Are you referring to Cascade Layers, or maybe something else?
With the inside-out view of HTML then CSS, it feels like that same stance gets muddied because of the technical aspects of the languages and their original intents.
Coding like this removes so much of the flexibility and power of CSS.
The reason we have media queries and now container queries and style queries is because HTML is for structure and CSS is for presentation and the state of things being presented.
If a user selects high contrast mode because they're sight impaired, that's a state issue that has to be handled by CSS; HTML can't help you here:
@media screen and (forced-colors: active) {
…
…
}
To anyone who's learned CSS properly sees this as "IF the user wants forced-colors mode (previously high-contrast mode), THEN execute the selectors between the braces."This is certainly cleaner than an explicit IF/THEN construct for this particular feature.
Gave up and am doing it like OP now. CSS MUST become a proper programming language (or at least non-turing-complete configuration language like dhall) to solve this.
These really arent decoupled.
I assume CSS was made very restrictive on purpose so that it can be applied to the page elements and analyzed very efficiently.
In the worst case just put a 3sec limit on the evaluation and stop it if it doesn't complete in time, problem solved.
That said, I do agree it would've been nice if they had the foresight to use `:has(:checked)` (or some kind of :if() syntax), but it's all hindsight 20/20 and it was the wild west on the web back when a lot of these features were spec'd/implemented. Kinda wish there was a new css version every 10 years or so to fix the inconsistencies (which you would have to opt into).
I think `p:empty { }` is a perfectly clear syntax. As the article says it's already a conditional statement - just imagine an invisible `if` in front of every selector or media query.
CSS is not a procedural language, so I think that if statements could be less clear about how rules are applied.
That reminds me of how I'm supposed to imagine an invisible watashi or an invisible ga in Japanese sentences, which is also incredibly difficult for for non-native speakers to do well (at least, in the beginning, which most do not get far beyond) and causes all sorts of problems for translation engines.
So how about making the language explicit, clear, and hence easier for humans to reason about? All this all you have to do stuff reminds me of how adherents speak about C and Git. Thank God we're seeing viable replacements for C, and I have hope in a replacement for Git (jj or pijul? I pray). If something comes along that is less painful than CSS I'll be the first to jump and I won't be alone.
Let's suppose we have a match function that could be used like this
:root {
--has-path: match(.modal .message .error);
}and then if the following were allowed (or some variation were developed where this could be done)
.user:has(var(--has-path)) {color: red}
I think it might be nice. YMMV however.
on edit: thinking about it however while it might be nice if you could do queries like that, it does seem like it would be expensive (for browser performance) to implement.
I'm not sure I totally understand the specifics of the example, but it's probably possible to get something close with the nested selectors spec being worked on now, and/or :is()
I figured it was implied some rules would have to be made which might be properties used in selectors are scoped to ancestors of element.
>I'm not sure I totally understand the specifics of the example
example is probably too fanciful but assuming you have a match of selector
.modal .message .error
then element with class user which is on a totally different part of the page - like
body
header
....stuff .user
/headerarticle
section
.modal .warning .error
then user has color red. Do I personally want this, not much, just considering the people who sometimes say they want if ( selector exists) then bunch of css.
another more likely usage for something like this (a match function that takes a selector) might be the various CSS logical operations things people do sometimes https://css-tricks.com/logical-operations-with-css-variables...
however, as I noted before I don't think it makes sense for css performance reasons, despite how interesting/amusing I might find the idea.
The other issue is that custom properties inherit, so now it's unclear what element you should fetch your var() from (you can't fetch it from the element you're styling, since you haven't computed its style yet). E.g.
.user:has(var(--has-path)) { --has-path: match(.something .else); }
which is why I specifically said it wouldn't work because of performance issues? This part here at the end >it does seem like it would be expensive (for browser performance) to implement.
>The other issue is that custom properties inherit
I'm not sure I see the problem with that actually -
If I set a value on :root and then override it on .foo then I thought it only got overridden for the descendants of .foo?
on edit: ah no of course you mean that if I set --base-color on .foo it is also available for .foo, but in that case I would think the rules would mean that use in selector was outside the scope of the element, that is to say scoped to the parent.
[1]: "The - -var: ; hack to toggle multiple values with one custom property"—https://lea.verou.me/2020/10/the-var-space-hack-to-toggle-mu...
I think this is a mistake in system design. CSS doesn't know or care or work with what a user is, what a foo is, what a bar is or how any of those interact conceptually. CSS operates on the rendered output of decisions made at that level: it shouldn't make those decisions, nor encode knowledge of those decisions. That's what JS is for, and what it's better at.
I think it makes much more sense architecturally to have JS respond to whatever condition triggered the error state by updating some metadata on everything involved (User{}, Booking{}, Etc{}).
```
// pseudo JS uiEvents.subscribe('error message', () => {
appState.User.hasBadData = true;
})uiEvents.subscribe('error message', () => {
appState.Booking.hasBadData = true;
})function validateForm(fields) {
fields.forEach(field => {
if(field.hasError) {
uiEvents.publish({some: "error message"});
}
}
}// pseudo React/Vue/whatever in pseudo TypeScript
render(({user, booking}: TAppState) => (
<>
<div class={`user ${user.hasBadData? 'user--error' : ''}`}>/\* user icons and text */</div>
<div class={`booking ${booking.hasBadData? 'booking--error' : ''}`}>
/* booking stuff \*
</div>
<>
});```
You could replace all the JS with server-side logic (which would probably take it closer to the "single source of truth" of users and booking anyway). Further, maybe you're making application with multiple communication modalities, and you want to play a sound or trigger speech when the user enters bad data, instead/as well as colouring everything red. CSS can't help you there. It's much better to keep the logic, state and behaviours out of the CSS and use it only to "dress up" the rendered markup, otherwise you make maintenance harder.