DEV Community

Cover image for A short & terrible history of CSS: Does it ever get better?
TJF
TJF

Posted on

A short & terrible history of CSS: Does it ever get better?

If you fell into a coma in 2005 and woke up today (yikes), what would you need to learn to catch up with modern CSS?

The first stop would have to be flexbox and grid — arguably the most important new features of the language. Well-implemented with a strong conceptual backbone and a consistent vocabulary, they (mostly) take the guesswork out of layout. We still can’t do proper masonry layouts and it’s hard to remember the difference between align-items and align-content, but at least we’ve left float behind.

HTML5 arrived with semantic elements, promising improved accessibility and machine readability. But more questions than answers resulted. Vague element names rooted in the world of print media were added: aside, section, and details appeared without a clear purpose in the application development landscape. Many of these elements brought a host of new default browser styling, completely undocumented. CSS resets emerged to cleanse the palate and bring consistency.

Media queries and relative units deserve mention for enabling responsive design amidst the switch to mobile, but the honor is dubious. Breakpoints and font-based scaling are crude tools in the fight to support a vast range of devices that come in every shape, size, and pixel density. Browsers have stepped in to help with device simulators, while frameworks like Bootstrap give folks a reasonable starting point — at the cost of injecting huge stylesheets that are difficult to customize or override. Collectively, humanity has probably lost centuries in the time spent struggling to make minor tweaks to Bootstrap forms.

On the build side, SASS and LESS have made it possible for CSS to support larger websites and applications, pretty much entirely by the virtues of nested selectors and reusable variables. Combined with hot reloading from tools like webpack and in-place editing with autocomplete through browser dev tools, the development flow for debugging and experimentation requires far less context-switching than it once did.

But the advanced features in preprocessors enable fragile anti-patterns that are difficult to debug and even harder to read. There are no established frameworks for managing stylesheets in large applications — inconsistent spaghetti is inevitable. Linters can offset some of these maintainability issues, but the global scope is inexorable. One typo in one rule can mangle any application’s display, and no test will be the wiser.

Thus, we find ourselves at the end of the timeline with CSS Modules and CSS-in-JS. These solve the scoping problem — a huge, necessary scalability win — but introduce new tribulations for reusability. Legibility in the DOM suffers as generated class names obfuscate intent. Performance drops as two decades of browser optimizations for rendering cascading stylesheets goes out the window. Separation of concerns dissolves as each component file now hosts a hundred lines of border-blah styles before the code even begins.

Congratulations! We’re all caught up on fifteen years of CSS. In total, we got two excellent specs for layout and a handful of tools that all fight each other. This a bit hyperbolic, of course (who could forget the indispensable page marks spec?), but we’ve mostly traded one set of problems for another. Just as we left behind the eternal struggle with Internet Explorer, mobile devices came in to detonate the possibility space.

The language simply hasn’t kept up — but why? CSS renders the entire web! Millions of programmers, designers, and unfortunate interns work with CSS every day. It’s worth our time to make this experience better; our creative capacity is limited by the quality of our tools.

Enough complaining, though. We know CSS is a bad experience — water is blue, the sky is wet. No one is coming to save us. What could we do to change this? Let’s think big. Full-throttle, pie-in-the-sky stuff.

  • Syntactic sugar. Nested and parent selectors supported in native CSS would be a great start; anything to lessen the need for preprocessors just to get the most basic quality of life improvements.
  • First-class IDE support. There’s plugins for intellisense and code peeking when writing classes and styles in HTML and JSX, but they’re stopped cold by any number of tools and dependencies that abstract styles away from markup. There’s autocomplete for writing directly in stylesheets, but these don’t provide context or definitions like a fully-featured language might offer. Fluency with CSS shouldn’t demand memorization of its entire vocabulary and logic.
  • Better specificity resolution. A relentless gotcha through the CSS experience is that collisions in specificity are resolved by using the last defined rule in the stylesheet. There’s nothing wrong with this on its own, except that a) it’s so easy to write selectors with equal specificity and b) order is not easily deterministic. With file concatenation and imported stylesheets from dependencies, we can’t rely on definition order. !important provides an escape hatch, but it’s an excessively nuclear option that breaks the most fundamental concept in CSS. Instead, a decorator applied at the selector or individual property level to give priority at its existing specificity could address this gap. More radically, we could define our own rules for precedence based on the needs of the application.
  • Customize the cascade. The rules of inheritance are countless, obtuse, and the single greatest source of confusion. Why does text-emphasis inherit but text-decoration does not? I’m sure there’s a valid reason somewhere in the story behind these specs — but as a consumer of this API, I don’t care. I need consistency and predictability. What if we could eliminate the guesswork and dictate inheritance per-property or per-selector?
  • Validation between styles and components. In React Native, you can get an immediate warning if you try to add text style properties to a non-text component — saving countless hours of fruitless tinkering. Currently, this kind of validation isn’t supported in HTML because any tag can (theoretically) support any property. If we could create and specify a style type for any component, this could prevent us from using invalid properties or values.

It turns out that someone else has already thought of this future: enter the CSS Houdini specifications, which offer direct access to the rendering engine. It enables custom parsing of selectors — a path towards syntactic sugar. It supports custom properties — a mechanism for controlling inheritance and default values. It provides typed object models for properties and values — style validation becomes possible!

When I started writing this piece, I had no idea this spec was under development— and I suspect this is true for many others. This great walkthrough by Tab Atkins currently has 32 views. This talk by Sam Richards has 900. Sure, some people are aware and excited — but the overwhelming perception I see among developers is that CSS as a whole is just legacy baggage, an atrophied limb of web development waiting to fall off.

I know better than to believe an incomplete spec — one that’s still in proposal and working draft phase after several years — is going to provide relief any time soon. Done properly, however, CSS Houdini could elevate the work of front-end design and UX development into something that is both more predictable and more expressive. After decades of spaghetti specifications and unreliable tooling, this is something worth being excited about in the ecosystem of web design.

Top comments (0)