Hacker News new | ask | show | jobs
by exogen 54 days ago
Been writing CSS since 1997. Your mistake is thinking that the authors of these frameworks (or their target audience) just don't know how to use CSS correctly. But it's far more likely that the authors know far, far more about the intricacies of CSS and how it works than most developers. These frameworks are almost always developed by people with deep experience in the trenches, who have realized that CSS scales fucking terribly... not because they are misinformed or did anything wrong.

Namely:

- Selectors are a global namespace. Imagine if every variable and function in your favorite programming languages were global and so had to be unique. No modules or namespaces. Developing a system to fix that would be pretty high on my list of priorities... coming up with a cool solution and then people telling me to "just learn the language" would be pretty fucking infuriating, don't you think?

- Several fighting priority systems (did you know about newer ones like @layer?). And equivalent priority falls back to source order - OK, so how do you square that with dynamic loading? Some navigation paths through an app would inject some CSS first whereas others would inject other CSS first. Good luck!

4 comments

> Selectors are a global namespace. Imagine if every variable and function in your favorite programming languages were global and so had to be unique.

A selector is not not a variable or a function. CSS has functions (e.g translate) and it has variables, which are both distinct concepts in the language from selectors.

> No modules or namespaces. CSS is not supposed to be a turing complete general purpose programming language. Why would you need namespaces and modules to style up HTML tags?

Because many web sites and apps aren't as simple as "my first homepage" and don't only consist of first-party code. Think component libraries. Reusable code. Content management systems. Third-party SDKs (chat widgets, support widgets, payment widgets like Stripe, etc.).

One of my earliest webdev jobs was at a company whose product was a widget you could add to your site by adding our `<script>` tag. Thus, our CSS needed to coexist with the first-party site's, not to mention any other third-party widgets on there. In other words: the same exact reason you need modules in traditional languages.

It's true in a case where you are doing the described thing you will need to come up with your own module system and ways to not step on other people's stuff but it isn't actually difficult. Although I have noticed some stories recently were quite big companies evidently didn't put in the work to keep from messing up other people's stuff.

Of course one drawback with that is you are also depending on developers and content managers at sites following your documentation on how to use your products, which is a different problem.

on edit: obviously if you have been writing css since 1997 and one of your first webdev jobs was this kind of thing, things were much more difficult back then. I did the same sort of thing in 2014-2015, not particularly difficult to make work. I worked web dev since 1999, first job was dynamic generation of web sites and other media from single source data.

> Because many web sites and apps aren't as simple as "my first homepage" and don't only consist of first-party code.

CSS is there to style HTML tags. Yes, your complex webb app is dealing with a lot of third party code and whatnot... but CSS is just there to style your HTML tags. Why is it so hard to get that?

Because that third-party code adds its own HTML tags.

Because that third-party code adds its own CSS.

Because my HTML and the third-party HTML live in the same document. Thus, my CSS could target their HTML, and their CSS could target my HTML. There are no modules and therefore no module boundaries.

Because my CSS and the third-party CSS share a global namespace. Thus, their CSS selectors might overlap with my CSS selectors. Hopefully nobody called anything ".container" or ".wrapper" or ".button" since there's no namespacing, right?

Keep in mind these components might be arbitrarily nestable. Maybe my elements can be a child of theirs? Maybe theirs can be a child of mine? Can I opt out of the cascade? Should I?

Maybe we could all use the Shadow DOM? I don't really have control over what third-parties do, but let's say I dig into their implementation (what fun) and choose only ones that use the Shadow DOM. Now I've massively bumped up the complexity and ensured my document can't render without JavaScript; lots of user-agents go right out the window and see nothing.

Let's reduce it to the simplest possible case: you and I are working on the same website, but I'm responsible for the header and you're responsible for the footer. We even have our own separate .css files. Yay, no merge conflicts.

Can you and I safely write our selectors without coordinating with each other?

(If you think the answer is yes... you're wrong.)

Now add a few dozen more team members and several third-parties who we in fact CANNOT coordinate with.

Everywhere you turn there are tradeoffs and half-solutions. Turns out "Just Do X" is not very helpful. Thus, I don't blame people at all for developing their own solutions to scaling CSS - like OP's framework.

> A selector is not not a variable or a function. CSS has functions (e.g translate) and it has variables, which are both distinct concepts in the language from selectors.

Congratulations, you have attacked the analogy rather than the argument.

So in your head, the analogy is not a big part of the argument? I'd accept your congrats, but I really have not earned it. The whole idea of comparing CSS to general purpose, Turing complete programming languages is surprisingly stupid. CSS has a very specific, narrow goal: styling HTML elements.
> So in your head, the analogy is not a big part of the argument?

The analogy is decoupled from the thing it analogizes, and refuting the analogy refutes the thing itself about as much as burning a picture of the thing burns the thing itself.

> The whole idea of comparing CSS to general purpose, Turing complete programming languages is surprisingly stupid. CSS has a very specific, narrow goal: styling HTML elements.

Right, so, you still don't like the analogy but again don't address the argument.

> A selector is not not a variable or a function. CSS has functions (e.g translate) and it has variables, which are both distinct concepts in the language from selectors.

That misses the point. They're identifiers scoped to the entire document - the same way global variables and global functions are identifiers scoped to an entire program.

The point is, comparing CSS selectors to the functions or variables in other programming languages is silly, cause guess what... CSS actually has functions and variables!
I am sorry that you are bad at analogies.
The problem is not CSS per se, but HTML.

HTML was designed to be a document language, like a PDF, but worse because it has no absolute positioning so text just changes shape when you change the width.

Almost every website does NOT follow the "document" paradigm but an "app" paradigm with navbars, sidebars, panels, etc. None of these things fit the model HTML was specialized for.

If HTML/CSS were for those things, we would have had scoped rules for classes day 1.

Exactly. The problem is the platform has not kept up with the way it's used in practice. Features are added with strange priorities, and obvious blind spots ignored, leading to it being under-developed in many areas, and over-developed in a handful of others. We get vendors working on weird stuff like Web Bluetooth before obvious things like element anchoring (finally, yay... but still just a Working Draft) and reliable element position tracking (still a huge pain).

There are still no affordances for many things, so people have to invent their own solutions.

I've always had the impression that the "problem" at its crux is that people worked around HTML/CSS limitations. A recent article about IPv4 gave me the same feeling.

When faced with a widespread standard that is sorely lacking, instead of patching the standard globally, people come up with their own workarounds. This accumulates until the whole thing becomes a conceptually "pure" core under a transfigured mess of patches to make it work the way the modern world needs it to work.

For HTML, I feel that PHP and Web 2.0 did unspoken harm to it. A lot of things that are done by server-side rendering (and afterwards in client-side rendering) should just be features built into HTML from the start. Templates, themes, widgets (panels), components, etc. Now we have "templates" and "components" but neither of these terms mean something in HTML that actually does what you would expect from a templating language which is critical of almost every website.

I mean, am I crazy to think that if you have 2 webpages that share the same navbar, you shouldn't need a build step or special program just to handle this use case in a way that is maintainable? It should just be built into HTML? Javascript isn't a solution to this because that means the website is unusable without Javascript, and that sounds stupid. You need to enable Javascript just to have a navbar? Or you need a whole SSR just to avoid having to copy-paste the navbar? Or a build step? How did we keep using this thing for 30 years?

I feel like because developers could just do this with PHP/JS, these basic features never made into the pure HTML language.

The weird thing is that these languages still get features, just not any features that makes it more powerful at building simple things. HTML has video, web assembly, CSS has animated fonts. But you still need a separate program if you want the same navbar everywhere.

- You could use 'CSS Modules' or 'CSS Module Scripts'; it's CSS but the classnames and animation names are locally by default.

- Rule priority is decided by selector specificity also.

HTML has also answered your these needs with the locally scoped ShadowDOM.

>Selectors are a global namespace.

what does this mean? Selectors are how you query a particular element to apply styles to it, I think what you mean is that selectors as a query language does not allow you to query any element from any other element the way you can with XPath but in many use cases you need to start querying globally.

but we can also see that the only way that querying from element to any other element makes sense if you could do something like (pseudo XPath / CSS abomination coming up)

.someclass[[ancestor::.mainEntry .headline:empty]]

or maybe you would prefer

.mainEntry[[.headline:empty]] .someclass

which sure, I have, some very few times thought boy it would be nice if I could do more involved context selection in CSS based on an elements surrounding DOM than sibling axis and the like offer.

however > Imagine if every variable and function in your favorite programming languages were global and so had to be unique.

yes, I know you are not saying that variables in CSS need to be global. However I think you have perhaps not internalized yet how CSS variables and scoping rules work have created a situation that allows for often elegantly solving problems that one might wish one had a more powerful query language to handle.

The following is of course not elegant, but is an example of how we change the design of .someclass based on having an ancestor mainEntry that has an empty .headline using variables.

https://codepen.io/bryanrasmussen/pen/xbEoabM

>OK, so how do you square that with dynamic loading? Some navigation paths through an app would inject some CSS first whereas others would inject other CSS first.

yes I've run into this situation before, however for me it was a framework issue not a css standards issue, and it sounds to me with phrases like "navigation paths" and "app inject some css" that it was a JS framework that made you unhappy and not actually the state of Web standards.

> .someclass[[ancestor::.mainEntry .headline:empty]]

> or maybe you would prefer

> .mainEntry[[.headline:empty]] .someclass

I don't really know xpath but isn't that this?

.mainEntry:has(.headline:empty) .someClass

D'oh! right, got lost in trying to think up a situation nowadays where you can't really do a meaningful query that takes all DOM context into account. Not sure if all edge cases are not actually handled by has and is, actually.
> However I think you have perhaps not internalized yet how CSS variables and scoping rules work have created a situation that allows for often elegantly solving problems that one might wish one had a more powerful query language to handle.

As you already said, this approach would work fine if you had no external payloads that inject their own HTML and CSS on the page, either at runtime or by being loaded by other modules in a CMS.

It also breaks when your :root selector is being polluted by overly generic variables by your platform, like in the most recent versions of Wordpress.