Pavel Panchekha

By

Share under CC-BY-SA.

What's Wrong with CSS?

None of the web technologies are great, but CSS is probably the least-complained-about.1 [1 CSS does not feature in the wat talk.] But that doesn't mean people don't complain! CSS is confusing, from how floats work to problems with interfering selectors. I've spent years doing academic CSS research, so I have some thoughts on what's wrong with CSS. But first—let's survey what others think.

If you're not interested in a dissection of the standard and just want to know why CSS is so weird, I recommend skipping to the Conclusion.

The CSS Working Group

The CSS Working Group publishes a semi-official list of mistakes. I recommend taking a look. The lack of organization or structure turns it into accidental comedy: mistake 1 is that nowrap should be spelled no-wrap, while point 4 is “Table layout should be sane”. Amen.

By my read, there are roughly four categories of mistakes in this list:

  • Spelling and names, like blend-mode instead of mix-blend-mode and the syntax of unicode ranges
  • Bad defaults, like in background-size or the border-box property
  • Conflated concepts, like white-space mixing white space and line wrapping or the meaning of :link
  • Plain bad decisions—more on this later

Of these four, the first is not worth fixing but minimally important.2 [2 They could add aliases to fix some of these, but that will add complexity.]

The second does lead to confusion (who likes clockwise edge order, for example?), but is also hard to directly fix. In some cases, new properties can let you “opt in” to better defaults, like border-box changing from the standard box model to the more intuitive IE model. More importantly, users can always fix an issue of bad defaults by writing out all the values and not relying on the defaults.

The third is harder to fix, because it's usually impossible to get the desired behavior in current CSS. But the Working Group is on it! In particular, the Houdini group is working on formalizing a bunch of lower-level CSS properties that touch a single concept each. A good example is the display property. In by-gone days, display had two values: inline, which means the element acted like formatted text, and block, which means the element acted like a paragraph. But later it turned out that sometimes you wanted half-inline and half-block elements: inline-block was born. inline-block made it clear that display conflated two concepts: how you look to your children and how you look to your parent. So in CSS 3, you can define those separately.

Most interesting is the fourth category of CSS mistakes. This is where the CSS Working Group really thinks it dropped the ball:

  • Table layout
  • Margin collapsing across zero-height boxes
  • The interaction of margins with min/max-height

Yeah—those are all bad. In table layout, the story is that different browsers chose to render tables differently, and the CSSWG could not get them to agree on a standard. So, amazingly, they formalized the table-layout property, which can be set to auto or fixed. auto makes correct layout “a matter of taste”, while fixed has defined behavior but looks consistently ugly. Popular guides say that the only use to use fixed is “is that the table renders much faster”.3 [3 Actually, I think sites that have large tables and a full-time web developer might want to look into fixed. It takes much more work to make it look good, but if you put in that work the speed might be valuable.]

Margin collapsing is another mess. Margin collapsing is the fact that, in CSS, adjacent vertical margins can overlap. It is usually the correct behavior! And back in the days before :last-child, this was the only way to create consistently spaced paragraphs. Today, I can still see the justification for margin collapsing, though it's a little weird that it only happens to vertical (not horizontal) margins; on the other hand, we have the tools to get rid of it entirely, if we were to value simplicity of spec over developer effort in the common case.4 [4 We don't.] But margin collapsing across zero-height boxes is a peculiar insanity. Since the top and bottom margins of a zero-height box are adjacent, CSS has them overlap. Where does that put the box, since it is now presumably between two overlapping things? Uhh… The CSSWG uses the only bold text on the page to call this the “root of all margin-collapsing evil”. I've racked my brains and I see no reason to allow margin collapsing over zero-height boxes, and as far as I can tell it all goes back to overly-general wording in the poorly-considered CSS 1 spec.

The interaction of margins with min/max-height is a pretty obscure mess, and here I'm not sure I agree with the mistakes list here. The issue is that the bottom margin of the last child of a block box can collapse with the bottom margin of its parent, since if nothing separates those margins they'll be adjacent. But what about the situation where we've got a box with minimum height 40 pixels, which has a 20-pixel-tall child with a 30-pixel bottom margin? Does the child margin leave the parent or no? (It does not.) The mistakes list suggests some sort of partial collapsing. That would make this case more natural, but it's not clear what the "correct" behavior in most cases is, so the behavior would probably still be confusing and the spec more confusing. Still the CSS folks are definitely right that this is pretty weird.

Alright, so that's the inside view from Working Group members. What about the rabble?

Hacker News

Luckily, the mistakes list was posted to HN, and the folks in the comments gave us their own take on what's wrong with CSS. I combed through the comments and tried to draw out some common thoughts.

Some comments argued for specific, pretty low-level decisions. These might seem petty, but I remember having trouble with most of these myself—people aren't crazy!

  • Using CSS from JavaScript is hard—hyphenated names and animation timing were called out.
  • Centering things is too hard—vertically, horizontally, you name it. It really is.
  • em is bad because it nests, it should do what rem does.
  • color is a confusing name (debate on better name rages in the comments).
  • What percentages refers to varies, and therefore is confusing and unpredictable.
  • Clockwise edge order is unpopular.
  • Linear color spaces for gradients leads to ugly gradients.
  • You mostly can't set percentage max-heights, even though CSS acts like you can.

Another common thread I characterize as a tug of war between designers and developers. Developers complain that CSS often makes it bizarrely hard to do pretty normal things like three-column layouts,5 [5 I remember the days when every issue of A List Apart had an article on three-column layouts.] pin striping, rounded corners, and drop shadows. The CSS committee is slow to add features that support this stuff, and browsers are slow to roll out support. Designers want CSS to more closely reflect the goal layout.

Meanwhile, developers want uniformity and simplicity. Developers complain about inconsistencies and suggest unifying CSS and JS or switching to constraint-based layout. Developers generally write CSS part-time and are more familiar with general-purpose programming languages, so developers also complain that the syntax is confusing, there's no modularity story, and that the language is large enough that you really can't learn all of it.6 [6 Of course, learning the whole, say, JS API, let alone all of Python (metaclasses) or C++ (ooh boy) seems difficult, so I think there's more to explore here on how to demarcate the "important part" of a large language so that it's more approachable.] A lot of tricks CSS adds in to make common designs automatic (like currentColor) confuse developers because they make the language larger. Plus, developers may not be conscious of standard designs, so the automatic behavior of CSS is just confusing.

Developers also don't like that rules, class names, and selectors are global. Different style sheets can and do interfere, leading to the common practice of adding or removing tag and class names from selectors to fix up the cascade order. It's useless busy-work, and it happens a lot in large applications or when styles are reused.

A common complaint was browser differences,7 [7 Most browsers implement most of the spec mostly-identically, but they have large and different default style sheets, so most pages look different across browsers.] which make it expensive to test designs and which clients complain about. It's commonly said these days that pixel-perfect design across browsers is not worth it, but that also frees browsers to misbehave and means the designer has to design fairly flexible layouts.

Finally, Hacker News commenters took tables, margin collapsing, and the interaction of different layout modes to task. These are definitely confusing, and CSS could handle them better; as we saw above, the CSS folks largely agree.

Formalizers

Formalizing a good chunk of the CSS standard has given me an interesting perspective on CSS's mistakes. When formalizing, the worst parts of the standard aren't the small idiosyncratic choices (like what lengths percentages refer to) but complex systems that interact. Plenty of these have come up in Cassius.

Tables, of course, are a mess, as described above. Tables look easy, but HTML allows you to nest tables, and that allows all hell to break loose. Plus, tables interact poorly with the CSS box model (consider border-collapse).

Floats have a fairly coherent mental model, but that mental model is pretty complex, and you wouldn't ever come up with it through trial and error. That means that while formalization was possible, using it is still trial and error (even for me).

Shrink-to-fit calculation is an odd example of something that pretty much always works how you expect, but has a pretty complex specification. The shrink-to-fit standard refers to the not-fully-specified table layout algorithm, and then lays out a spec that seemingly requires you to render shrink-to-fit elements three times. Needless to say, actual browsers don't do that, and instead use some sort of undocumented approximation to this algorithm. It would be good to standardize what browsers use.

Text is crazy complicated, and mostly unstandardized. Different scripts work quite differently, and customs for mixing them are even weirder. For example, in mixed Chinese/Latin text the Chinese character sit slightly below the Latin baseline, and Tibetan is even weirder since its characters hang down from the baseline instead of sitting on it. The WHATWG has a helpful diagram. Not only is text complicated, but browser don't make font metrics available to JavaScript, so the whole thing is a little opaque. And, of course, different users have different fonts, fonts render differently in different OSs, and font designers really want the flexibility to make an 18 pixel font 16.8 or even 12 pixels tall.

Finally, different layout modes interact in confusing ways. For example, the content of inline-block boxes mostly ignores the stuff outside it, but text baselines do leak in. Or, floats that are generated by elements in the middle of a line of text can go either before or after that line of text, another hard-to-predict thing. Considered broadly, you might also say that scrollbars (underspecified) and min/max height (confusing interactions with a lot of stuff) are also layout modes that play poorly with others.

I think these are interesting, but overall I don't think the perspective of formalizers is valuable.

Conclusion

I think a lot of what is confusing about CSS does not stem from accidents the Working Group has made over the years. Instead, I think many of these issues stem from some very difficult constraints CSS places on itself:

  • Every CSS file produces a rendering for every HTML file. No error is ever thrown.
  • CSS is instantaneous:8 [8 Except for animations, and the impedance mismatch is much noted.] nothing in CSS happens "before" or "after" anything else.
  • CSS is permanently forward and backward compatible.

These constraints stem from the needs of users, who are unfamiliar with debuggers, don't like version conflicts, and would always prefer a broken layout to an error message.9 [9 XHTML didn't take off, either.] And I think it is very much the spirit of the web to stick to these rules. Web apps get nothing right except distribution; but distribution is the most important problem, so web apps rule.10 [10 On the desktop. I believe their failure on mobile is a combination of the Working Group moving slowly and the slow processors of early smartphones.] Likewise, if CSS gets nothing right except making sure you see the information you came to see, maybe that's all it needs to get right. The web is user-first, and I think it's something to be proud of.

But I think a lot of criticism of CSS is also misplaced. CSS has to support common Western design practices that evolved over centuries: run-in headings, indented paragraphs, floating figures, line wrapping, text columns, and of course text layout itself. You can design a very nice and simple styling language if you don't plan to support any of that, but it won't meet the real goal of making rich visual design possible. Some purists think rich visual design is worth sacrificing in the name of simplicity. I don't agree, and I think the explosive growth of UI design over the last decade suggests that users don't agree either. For example, constraint-based layout is a popular solution for layout (it's even widely used on iOS) that is pretty mathematically clean. But text wrapping around figures is real hard to do in constraint-based layout!

The issue is that while centuries of print design have invented a lot of visual layouts that we'd like to use, it hasn't had to hammer down how those layouts work in extreme situations. For example, in justified text with very long words, CSS must still produce some rendering, leading to words sticking out the ends of lines or to very large gaps between words. In print, you'd handle this case by rewriting the text and manually hyphenating the words; CSS can't pause and ask the author to do that.

The same goes for the complicated float rules. Few print publications have multiple weirdly shaped figures float next to one another: it looks ugly, so layout will manually move the floats apart, or rearrange the figures into a figure page. But CSS again needs to do something, even if it's not a particularly smart thing. If CSS could throw errors, horizontally adjacent floats could just throw an error. But there's no one around to catch and resolve the error!

On the other end of the spectrum, CSS can't just be a collection of common designs to choose from. Web design isn't just choosing a Squarespace template! We need reusable tools that allow designers to create new designs without waiting for a committee to standardize them, and that means lots of little layout mechanisms that work together somehow. We allow new combinations because they might be really coherent, and future designers might use them, but for the same reason they might be totally incoherent.

At its core, perhaps the original sin of CSS is trying to meet incompatible demands for simplicity (from developers), flexibility (from designers), and responsiveness (from users). And the miracle of CSS is that it still mostly works. The web is mostly smart, beautiful, and user-controlled, without being fully any of those.

Footnotes:

1

CSS does not feature in the wat talk.

2

They could add aliases to fix some of these, but that will add complexity.

3

Actually, I think sites that have large tables and a full-time web developer might want to look into fixed. It takes much more work to make it look good, but if you put in that work the speed might be valuable.

4

We don't.

5

I remember the days when every issue of A List Apart had an article on three-column layouts.

6

Of course, learning the whole, say, JS API, let alone all of Python (metaclasses) or C++ (ooh boy) seems difficult, so I think there's more to explore here on how to demarcate the "important part" of a large language so that it's more approachable.

7

Most browsers implement most of the spec mostly-identically, but they have large and different default style sheets, so most pages look different across browsers.

8

Except for animations, and the impedance mismatch is much noted.

9

XHTML didn't take off, either.

10

On the desktop. I believe their failure on mobile is a combination of the Working Group moving slowly and the slow processors of early smartphones.