DEV Community

Cover image for The Two-Tree Problem with Styling on the Web
Daniel Nagy
Daniel Nagy

Posted on

The Two-Tree Problem with Styling on the Web

https://danielnagy.me/posts/Post_jt4adn0o5bnr

TL;DR

In this article, I discuss what I call the "two-tree problem" with styling modern web apps. The problem arises from the separation of structure and style.

I suggest that while CSS is a well-designed language for styling, the cascade and separation of styles from HTML may have been mistakes.

I then examine some popular styling libraries, including Emotion, Styled Components, Tailwind, and UI Box, assessing their effectiveness in addressing the two-tree problem.

I make a case for inline styles as a potential solution, emphasizing their simplicity. I acknowledge the limits of inline styles but suggest that these limits might not be significant issues for modern web development.

Top comments (5)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

The problem with styling in the modern web is that people don't know CSS and don't want to bother learning it. There's been a thousand attempts of people to get around actually learning CSS and it always ends up only creating more problems.

The way to "fix" this is for programmers to stop telling themselves CSS is beneath them and just grow up and learn CSS.

I know a lot of you are rolling your eyes right now because inline styles do not have feature parity with CSS

No. We're rolling our eyes because inline styles, that is, inlining your styling into the markup, whether that be using style attributes or building a custom styling system using class instead, is a harmful antipattern.

CSS is the way to go; anything else is just a tool to get away without learning proper web development. Just like WYSIWYG editors, they might be great tools for those who just decide to make that trade-off; but any self-respecting professional web developer should really just learn their job properly.

Collapse
 
ademagic profile image
Miko

Modern CSS has come so far from the CSS2 of yore; to me the only thing that these tools do is help to explore how CSS could grow. Which is not to say that it's broken, or that the tools are better than CSS; rather that tools become old, outdated, unmaintaned, unpopular, bested -- but CSS will be improved. IMO that's all these "new standards" do. Improve the actual standard.

Wouldn't say learning other libraries is not "learning your job properly". Learning core web tech always pays though.

Collapse
 
efpage profile image
Eckehard • Edited

For me CSS has some serious issues.

1 . Lack of fine grained scoping.

Most programming languages - as an example - know ways to define scopes implicitly. You can define properties inside a class that are global to the class members, but local to the global context and so on. Modern frameworks like Svelted use a similar approach by defining CSS inside modules. CSS3 brings some better options, but there is still nothing like local scopes available, that are automatically bound to a component.

2 . Global names

As all identifiers are global scoped, it is still not possible to fully avoid naming conflicts using external modules or libraries.

Many tools around try to solve this issues, but at the same time bring a lot of overhead and new challenges

Thread Thread
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

Maybe I misunderstand your problem with scoping, but isn't @scope basically just that? Also, in a sense, custom properties also provide a similar mechanism, in that they can be set on an element and are accessible from any child element, kind of like overriding static properties on a class in imperative languages.

The second point seems a bit more complex, because "scope" can mean several things here; there's the DOM structure, and there's the CSSOM. Scoping in terms of DOM is already a thing. It used to be limited to descendant combinators, but the introduction of @scope will solve the donut hole problem, so I'd say that side of "scoping" is basically solved.

Then there's the the CSS side of things, where "scoping" hasn't historically been a thing because CSS, by nature, is meant to be "global", and naming conflicts weren't really a meaningful concept, as several rules independently targeting the same element is an integral part of how CSS works; the only way in which I currently see a problem with this is custom properties, the semantics of which are defined by the CSS author, and in the future also mixins and functions.

One module may define --radius on an element and use it for one thing, the next may define --radius on :root and use it for something else. This would then cause problems because this one property now has two separate effects, because the variable is only scoped in terms of DOM, but not in the CSSOM.

This is a relatively new problem though, which only results from the introduction of more complex features, so it's reasonable to assume CSS will eventually sort these out, and only hasn't done so yet because the problem itself is will only surface with the adoption of these new features so there is no real world experience to inform what a good solution would look like.

Personally, I'd like to see lexically scoped symbols of some sort, so you could do @symbol [radius]; to define a unique symbol that is lexically scoped, then use it with --[radius]: .2em; and be 100% sure no other custom property will ever clash with that one because --[radius] in one file is different from --[radius] in another file.

Thread Thread
 
efpage profile image
Eckehard

Maybe I misunderstand your problem with scoping...

Scoping is the mechanism that controls the range in which rules are applied or definitions are valid. CSS has definitively made a nice progress in this field, as the initial approach of the "cascading" styles, that are applied to all children, often caused more trouble than it solved. So, the new "@scope" is a real step forward, but it is still some kind of "explicit scoping". You will need to name scopes manually.

I´m just comparing this to what we have in other languages, as any kind of programming language uses scopes:

  • In most languages we know a global scope, as we had in CSS all the time
  • Usually we have some kind of "local scope", which is different from inline-styles. A local scope is bound to a logical unit like a function or a class, inline styles are just bound to the element and have no real "range" (beside the direct children)
  • In OOP-languages scopes can be bound to a class. So all Objects derived from this class will automatically inherit this scope. I call this "logical scoping", as the scopes follow the logic of your code. This is often used to control the appearance of element families in UI´s.

CSS is still missing this level of "implicit scoping" which is a reason why people use CSS in JS solutions. Implicit scopes are much easier to maintain, as you do not need to care for naming. If a definition only makes sense in a certain context, it is useful that this definition is only valid there. Even with the new "@scope" you can apply a definition anywhere (maybe accidentially by typing a wrong letter), regardless if it is useful or not. This does not mean you cannot make any errors. It is just much easier if you just use named scopes.

Svelte introduced CSS that is bound to a module, which is a bit more like what we have in other languages. CSS in JS goes a similar way, as it allows to bind CSS to functions or classes, but as we see, this might come with some serious performance costs.