> I'm fascinated by the fact that so many of my fellow programmers - often the smartest ones - have so much trouble with CSS. I'm not sure exactly what the reason for that is
I will demonstrate why.
First, think of all the tools that you use that require a text interface. That may include a programming language (python, ruby, javascript, etc), a command line shell (bash, fish), a database client (mysql, psql), a configuration script language (systemd, nginx), etc. In all these the text based interface is the language used to speak with the underlying engine. Now, these languages must be designed and there are good designs and there are bad ones. If the language sucks, people can generally just choose to go with a competing alternative that has a somewhat more accommodating syntax. But CSS is a monopoly. We're stuck with it. For many of us it's a really shitty language that is guaranteed to live on, no matter what, just because, well browser technology.
Ok, enough ranting, on with my demonstration.
For this I'll take CSS positioning as a case. Let's say we decided to design a domain language to express how items on a web document must be placed. We could start from the perspective that when repositioning an element it should always be relative to some point of reference. That starting point could be the element's default position, or it could be the viewport, or it could be an ancestor element. From there we could devise some simple rules to express this.
To move an element you must first declare where you're starting from. For example moving with respect to its "default" position in the normal flow of the page would be declared as such (could be made optional too):
position-reference: default
To move with respect to the viewport, you would start with:
position-reference: viewport
To move with respect to an ancestor (and possibly even a distant cousin element):
position-reference: parent /* immediate parent */
position-reference: div /* first div ancestor */
position-reference: root /* the document */
position-reference: <some-syntax-to-navigate-ancestry>
From there you could add a number of extra properties to fine-tune some specific behavior. For instance if you need to maintain the space that the element was occupying previously:
position-maintain-gap: true
Or to prevent scrolling when the item is relative to the viewport:
position-scrolling: off
The above patterns might seem vaguely familiar to some. That's because I've just described "relative", "sticky", "absolute" and "fixed" positioning (albeit with some added possibilities). As someone used to interact often with text-based interfaces, if I didn't know CSS, the above would be an approximation of my expectation of its rules for these concepts. This is how I understand them and how I anticipate a good domain-specific language would allow me to express them. It's precise and descriptive, but more importantly it's not too idiosyncratic, meaning that I'm not making up terminology that is so foreign that occasional users will have to be given a refresher every time they need to use them.
Now, contrast that with how these concepts are actually expressed in CSS. Instead of the different patterns being simply given as combinations of the above simple options, as I've demonstrated, they're rather given as ready-to-eat dish with labels such as "relative", "absolute", "fixed", "sticky". The whole thing is then sprinkled with ifs and buts. This "simplification" doesn't always have the intended effect though, and many many people are just confused when first trying to understand positioning.