After my post the other day about how I got started with CSS 25 years ago, I found myself reflecting on just how far CSS itself has come over all those years. We went from a multi-year agony of incompatible layout models to the tipping point of April 2017, when four major Grid implementations shipped in as many weeks, and were very nearly 100% consistent with each other. I expressed delight and astonishment at the time, but it still, to this day, amazes me. Because that’s not what it was like when I started out. At all.
I know it’s still fashionable to complain about how CSS is all janky and weird and unapproachable, but child, the wrinkles of today are a sunny park stroll compared to the jagged icebound cliff we faced at the dawn of CSS. Just a few examples, from waaaaay back in the day:
- In the initial CSS implementation by Netscape Navigator 4, padding was sometimes a void. What I mean is, you could give an element a background color, and you could set a border, but if you adding any padding, in some situations it wouldn’t take on the background color, allowing the background of the parent element to show through. Today, we can recreate that effect like so:
border: 3px solid red; padding: 0.5em; background-color: cornflowerblue; background-clip: content-box;
Padding as a void.
background-clipin those days, and backgrounds weren’t supposed to act like that. It was just a bug that got fixed a few versions later. (It was easier to get browsers to fix bugs in those days, because the web was a lot smaller, and so were the stakes.) Until that happened, if you wanted a box with border, background, padding, and content in Navigator, you wrapped a
<div>, then applied the border and background to the outer and the padding (or a margin, at that point it didn’t matter) to the inner.
- In another early Navigator 4 version, pica math was inverted: Instead of 12 points per pica, it was set to 12 picas per point — so
- Navigator 4’s handling of color values was another fun bit of bizarreness. It would try to parse any string as if it were hexadecimal, but it did so in this weird way that meant if you declared
color: inheritit would render in, as one person put it, “monkey-vomit green”.
- Internet Explorer for Windows started out by only tiling background images down and to the right. Which was fine if you left the origin image in the top left corner, but as soon as you moved it with
background-position, the top and left sides of the element just… wouldn’t have any background. Sort of like Navigator’s padding void!
- At one point, IE/Win (as we called it then) just flat out refused to implement
background-position: fixed. I asked someone on that team point blank if they’d ever do it, and got just laughter and then, “Ah no.” (Eventually they relented, opening the door for me to create complexspiral and complexspiral distorted.)
- For that matter, IE/Win didn’t inherit font sizes into tables. Which would be annoying even today, but in the era of still needing tables to do page-level layout, it was a real problem.
- IE/Win had so many layout bugs, there were whole sites dedicated to cataloging and explaining them. Some readers will remember, and probably shudder to do so, the Three-Pixel Text Jog, the Phantom Box Bug, the Peekaboo Bug, and more. Or, for that matter,
- And perhaps most famous of all, Netscape and Opera implemented the W3C box model (2021 equivalent:
box-sizing: content-box) while Microsoft implemented an alternative model (2021 equivalent:
box-sizing: border-box), which meant apparently simple CSS meant to size elements would yield different results in different browsers. Possibly vastly different, depending on the size of the padding and so on. Which model is more sensible or intuitive doesn’t actually matter here: the inconsistency literally threatened the survival of CSS itself. Neither side was willing to change to match the other — “we have customers!” was the cry — and nobody could agree on a set of new properties to replace
width. It took the invention of DOCTYPE switching to rescue CSS from the deadlock, which in turn helped set the stage for layout-behavior properties like
I could go on. I didn’t even touch on Opera’s bugs, for example. There was just so much that was wrong. Enough so that in a fantastic bit of code aikido, Tantek turned browsers’ parsing bugs against them, redirecting those failures into ways to conditionally deliver specific CSS rules to the browsers that needed them. A non-JS, non-DOCTYPE form of browser sniffing, if you like — one of the earliest progenitors of feature queries.
I said DOCTYPE switching saved CSS, and that’s true, but it’s not the whole truth. So did the Web Standards Project, WaSP for short. A group of volunteers, sick of the chaotic landscape of browser incompatibilities (some intentional) and the extra time and cost of dealing with them, who made the case to developers, browser makers, and the tech press that there was a better way, one where browsers were compatible on the basics like W3C specifications, and could compete on other features. It was a long, wearying, sometimes frustrating, often derided campaign, but it worked.
The state of the web today, with its vast capability and wide compatibility, owes a great deal to the WaSP and its allies within browser teams. I remember the time that someone working on a browser — I won’t say which one, or who it was — called me to discuss the way the WaSP was treating their browser. “I want you to be tougher on us,” they said, surprising the hell out of me. “If we can point to outside groups taking us to task for falling short, we can make the case internally to get more resources.” That was when I fully grasped that corporations aren’t monoliths, and formulated my version of Hanlon’s Razor: “Never ascribe to malice that which is adequately explained by resource constraints.”
In order to back up what we said when we took browsers to task, we needed test cases. This not only gave the CSS1 Test Suite a place of importance, but also the tests the WaSP’s CSS Action Committee (aka the CSS Samurai) devised. The most famous of these is the first CSS Acid Test, which was added to the CSS1 Test Suite and was even used as an Easter egg in Internet Explorer 5 for Macintosh.
The need for testing, whether acid or basic, lives on in the Web Platform Tests, or WPT for short. These tests form a vital link in the development of the web. They allow specification authors to create reference results for the rules in those specifications, and they allow browser makers to see if the code they’re writing yields the correct results. Sometimes, an implementation fails a test and the implementor can’t figure out why, which leads to a discussion with the authors of the specification, and that can lead to clarifications of the specification, or to fixing flawed tests, or even to both. Realize just how harmonious browser support for HTML and CSS is these days, and know that WPT deserves a big part of the credit for that harmony.
As much as the Web Standards Project set us on the right path, the Web Platform Tests keep us on that path. And I can’t lie, I feel like the WPT is to the CSS1 Test Suite much like feature queries are to those old CSS parser hacks. The latter are much greater and more powerful than than the former, but there’s an evolutionary line that connects them. Forerunners and inheritors. Ancestors and descendants.
It’s been a real privilege to be present as CSS first emerged, to watch as it’s developed into the powerhouse it is today, and to be a part of that story — a story that is, I believe, far from over. There are still many ways for CSS to develop, and still so many things we have yet to discover in its feature set. It’s still an entrancing language, and I hope I get to be entranced for another 25 years.