An experiment in containing stylesheet complexity.
This post is part of my personal notes about the benefits currently specified in Shadow DOM, but contentious and held up in committee. We’ll work it out in standards, I’m sure — but given the number of things Shadow DOM was addressing, it may still be several years until we have solutions widely implemented and deployed that solve all of them. This has me doing a lot of thought exercises about what can be done in the meantime. The following reflects one such exercise: specifically, what would it mean to solve just the styling end of this on its own. Warning: it may be mildly crazy.
The Pandora Box
CSS works really well if you can follow good patterns and have nice rich markup. It lets you define broad rules and inherit and override selectively, and if used well it cleanly decouples a separation of concerns — it’s pretty elegant actually.
On the other hand, in the real world, things are often not that simple: until pretty recently, HTML wasn’t especially expressive natively, so we worked around it — many times on our own by adding classes like “article.” But, there wasn’t a standard. Likewise, our ideas about the patterns we should follow or best practices continues to change as we gain new powers or spend more time with the technology. Of course, there are a vast number of cases where you’ll just go and upgrade your philosophy and be the better for it, but there are a number of times when this just isn’t an option. This is sometimes referred to in standards circles, less colorfully, as “the composition problem.”
When it comes to these sorts of cases, quality and philosophy are inevitably mixed and not entirely in the control of any one team. In these sorts of cases, CSS selectors are kind of like live hand grenades, it’s just way too easy to do damage you didn’t intend to do because it requires a level of coordination of approach which is, for business reasons, highly impractical. And so you try really hard to analyze the problem, get the right select/cascade/descend/inherit/specificity, pull the pin, lob it into the pages and… hope. Frequently, all hell breaks loose. The more you mix it up, the harder it gets to reason about and the more our stylesheets explode in complexity. You’ve opened the proverbial Pandora’s Box.
A few best practices for when you're learning the language
Getting started with the DOM is easy once you understand how the browser translates your HTML into this internal structure made of objects. Once these objects are created, then you can manipulate them using a wide variety of properties and methods, to change the content of an element, to add a style to an element, or even remove an element from the page completely.
The DOM and human nature create some challenges
“Design by Committee” is rarely a compliment. Can the Web shift away from that model, retaining some order without falling into troublesome chaos?
Part of the excitement around the Extensible Web Manifesto was that it wanted to move the Web to more of an evolutionary model:
We aim to tighten the feedback loop between the editors of web standards and web developers.
Decorating content may no longer be enough
Thousands of people invented it independently. Millions use it without thinking about a broader context. It’s time to name it so we can talk about it.
Transformation is changing the way we look at the balance between clients and servers, our approach to formatting and layout, and our expectations of what’s possible on the Web. As applications shift from transformation on the server toward transformation on arrival on the client, transformation’s central role becomes more visible.
These practices have been emerging for a long time, in many different guises:
- In the Dynamic HTML days, scripts might tinker with the DOM tree as well as modify CSS presentation.
- Transformation was supposed to be a regular and constant thing in the early XSLT plans. Stylesheets on the client would generate presentation from clean blocks of XML content.
- New data format options evolved at about the same time that Ajax emerged. JSON offered a more concise set of programmer-friendly content tools. Many apps include a ‘bind JSON to HTML before showing it to the user’ step.
- Template systems now run on the client as well as the server. In many systems, templates on the server feed data to the client, which applies other templates to that data before presenting it to users.
- The HTTP powering Ajax still created a long slow cycle of interaction. WebSockets and WebRTC now offer additional approaches for collecting content with less overhead, making it easier to create many more small transformations.
- Some developers and designers have long thought of the document tree as a malleable collection of layout boxes rather than a deliberately coherent base layer. Separation of concerns? A dead horse, apparently. Recent debates over CSS Regions highlighted these issues again.
Look beyond jQuery to an MV* approach
You’ve probably seen seen simple example editors, where the browser acts as an editor for to-do lists. In these applications, you edit to-do items, consisting of a text and a state (pending or done). These small editors are very helpful for studying monitoring events from the keyboard and partially updating page content. These are the main principles for building applications in web browsers.
Once you going beyond to-do list editors, the requirements quickly increase. For example, you might work with multiple counters that observe the cursor position, or the number of words in a text box. You might need to support multiple editing modes or text formatters, or edit actions and state synchronization with a backend.
One speaker at Fluent 2013 whose talk was particularly well received was Todd Kloots of Twitter who spoke about HTML5’s pushState API and demonstrated how it was used in Twitter’s Web-based interface.
Some key parts of Todd’s talk include:
- The opportunity Twitter saw in pushState [at 01:45]
- What you had to do with dynamic URLs before pushState [at 02:46]
- A summary of the pushState API [at 06:10]
- Gotchas and browser support [at 07:58]
- How pushState sped up navigation on Twitter.com without re-architecting [at 12:15]
- What Twitter had to do server-side to make progressive enhancement work [at 19:11]
- Final thoughts [at 31:37]
- Q&A [at 32:15]
jQuery makes it really easy to work with the DOM and other browser APIs. Almost too easy. Having the almighty
$ available to you at all times can lead to an architectural style that I refer to as “jQuery soup.”
A jQuery soup codebase is one where adhoc references to
$ appear everywhere. AJAX calls and DOM manipulations are intermingled alongside application logic and business rules.
$ is essentially mutating a big bag of shared global state. That means that whenever you want to modify or extend that part of the app you need to carefully maintain a large mental model of every DOM interaction in your head. That’s really hard to do, and very much prone to error.
This summer, I’ve seen all kinds of programming approaches as I’ve bounced between the Web, XSLT, Erlang, and XML, with visits to many other environments. As I look through the cool new possibilities for interfaces, for scaling up and down, and for dealing with data, I keep seeing two basic patterns repeating: walking trees (of data or document structure), and handling events.