I've got some ambitions plans for CSS this year, and I thought it would be useful to get them out there and socialize them before I start working on them in earnest.
My overriding goal this year is to pursue generative solutions to problems. CSS has a complexity problem, and I've been tackling it from the sides for a while, but my approach doesn't scale. If CSS is supposed to grow out to satisfy all the demands that people put on it, our current modus operandi is insufficient. We too often solve the specific problem in front of us, too scared of overreaching to solve a more generic instance of the problem. This makes CSS a hodgepodge of well-designed (when we're lucky) but overly-specific solutions, which increases the complexity debt. Worse, if you want to stray too far outside of the bounds of what we provide, you have no recourse - you have to start over from the beginning, re-implementing everything in a preprocessor or in JS and building back up to where you were, rather than just inserting yourself at the point you want to change and flexing.
The Javascript community had this same problem, and they're consciously steering toward providing new, powerful primitives that let you insert yourself right where you want more easily, like Proxies, Maps, and others, and ensuring that a lot of new syntax is just desugared into old syntax, so that with a bit of research you can make a precision edit at the right place and achieve your goal without having to rebuild it all yourself.
So, what does that mean for CSS? Lots, unfortunately. ^_^ Here's an incomplete list of the kinds of things I want to work on this year (and probably for the next several).
Animations
CSS Animations are great, when your problem is shaped exactly like the problem they were designed to solve - a static animation known at page-load time. As soon as you step outside of that, you either have to do a lot of silly and inconvenient string manipulation, or you switch to JS completely and do setTimeout/requestAnimationFrame-based animations, which are inherently jankier and burn more battery.
I'm trying to fix this in a few ways. One, I'm helping in the Web Animations effort, which is a spec that redescribes CSS Transitions and Animations (and SVG animations, while it's at it) in terms of JS-exposed primitives. You can grab and manipulate CSS animations in JS in a really easy, intuitive manner, and importantly, you can construct your own animations with very little effort (and we're making sure there's sufficient sugar so it should be about as easy to use as jQuery's animate() function in common cases). These animations get run by the browser, and so are inherently smoother and more efficient, and in many cases can be run on the compositing thread (for browsers that use one) for really jank-free animation.
In another realm, which doesn't sound like it's related but totally is, I'm trying to fix how we respond to touch gestures. Again, we're stuck right now with having to use JS, which adds unavoidable delay and jank between our finger and the page's response. I'm talking about simple things like having an infinite-scrolling image carousel (that loops back to the beginning when you hit the end, so it's not just scrolling), or Android's pull-down notifications drawer, or iOS's swipable folders, or Twitter's "pull down to refresh" gesture. All of these have a pretty simple unifying concept - you make a gesture of some sort, and your progress in that gesture corresponds to one or more elements working through some static, known-ahead-of-time property changes. This looks suspiciously like a CSS Animation, and it can be! The only thing needed to make this all work is allowing an animation to listen to alternate "time sources" aside from the document, such as the progress of a scroll-down gesture, or a pinch-zoom gesture, or a rotate gesture. I have a very simple proposal to solve this (and it ties in well to the concepts that Web Animations is using), but I haven't been able to sell it to the CSSWG yet. :/
Layout
Layout is one of the most complicated parts of a page from a coding perspective, but so simple mentally. For forever we've been locked into layout modes that are intrinsically document-centric, designed for relatively linear flows of text and images. We've collectively come up with amazing hacks to get around the limitations of these layout systems, but they're hard to use, brittle, and not very powerful anyway.
I'm attacking this problem head-on with the Flexbox and Grid specs, for 1d and 2d layout respectively. These new layout modes are explicitly designed to work for applications, not documents, and so give us a lot of the simple tools that we've wanted for forever, like powerful sizing and alignment abilities (look ma, vertical centering!), and free us from the assumption that code order is the same as the desired display order.
Both of these, while great, are still fairly specialized. Sometimes you need to break out of the defined layout modes entirely, and roll your own in JS. No matter how well we do, there will always be times when this is necessary. Unfortunately, doing this isn't that great today. We can't optimize your JS-run layout as well as we optimize the CSS layout modes. I'd like to fix this, so that JS-based layout is a lot simpler and more efficient. The idea I'm pursuing right now is a new "display:viewport" value which imposes really strong isolation constraints on the element and its children, effectively making it a replaced element as far as the rest of the page is concerned. This lets us ignore whatever inefficient stuff you do inside of the viewport, and avoid doing unnecessary whole-page relayouts because we can't be sure your changes are safe. If done properly, this will allow some really strong optimizations too, such as allowing the browser to destroy renderers for elements that are too far off the screen, so you can put a lot more stuff in the element and still maintain good performance. The end-game here is to make the isolation guarantees so strong that the browser can destroy and recreate the DOM elements themselves as it deems necessary, because this is something that high-performance webapps with large amounts of data already do by themselves, and we have the potential to make it easy, automatic, and even more performant.
Polyfills
JS has a strong history of "polyfills", hand-rolled replacements for features that are either still in specs or that are only in modern browsers, so you can use the feature everywhere. For example, the new JS Maps feature can be polyfilled with a slower variant (O(n) lookups rather than O(1)) that uses two arrays internally, one for keys and one for values.
HTML has also had this, though it was weaker, with the ability to use arbitrary attribute names and even element names in your document. In recent years this has been explicitly embraced - HTML added the data-*
family of attributes specifically to allow people to add their own attributes without accidentally blocking us from adding a new attribute name in the future, and Web Components allows, among other things, authors to mint their own tag names (the only requirement being that they include a - in the tagname, to distinguish it from existing and future HTML elements).
CSS, though, has been actively hostile to polyfills, except in the form of server-side preprocessors. I think there's good reason for this - like HTML, we worry about some custom tagname getting popular, and preventing us from using that name for a similar standards-based feature. So, whenever CSS encounters something it doesn't understand - an at-rule, a selector, a property name, or a value - it just drops it on the floor, rather than holding onto it and letting JS potentially find it and fill in the functionality some other way.
I'd like to fix this, in a way similar to HTML, so we can safely polyfill functionality in CSS without the risk of blocking future development. This is already partially done, with CSS Variables. The custom properties that define variables must start with "var-", but then can have any name and any value, and are retained by the style system. They're intended to be referenced in a real property's value with the var()
function, but they can be put to other uses as well. If you just treat "var-" as an author-prefix, similar to a vendor prefix like "-moz-", you can use custom properties for polyfilling CSS properties - just write all the polyfills with the var prefix, and have JS iterate over the stylesheet looking for them afterward.
For many properties, though, we don't really need the full power of JS to polyfill them, and so it would be convenient to use pure CSS. For this, I suggest adopting something like SASS's @mixin rule, which can take arguments and then expands into one or more properties in-place. This happens to do more than just property poly-filling, too - mixins can polyfill entire style rules or at-rules, as evidenced by Compass's impressive library of tools.
This isn't enough, though - the other types of things you might want to polyfill are still inconvenient or impossible, which I'd like to fix.
We can make individual values polyfillable through custom functions, a la SASS's @function rule. They'd work similarly to CSS variables, where you just substitute the return value of the function in the property where it appears. We can probably do a lot here with pure CSS, like SASS does, perhaps with some additions to calc() to make more powerful calculations possible, but this ability seems to also beg for the ability to use a JS function to fill in values, perhaps on a per-rule basis.
Making selectors polyfillable is very similar - we can define a syntax for user-defined pseudoclasses, and provide both a means of supplying simple definitions in CSS (for simple ones, like copying's jQuery's :header which just matches h1, h2, etc.), and evaluating them in JS somehow. I've seen some very reasonable proposals for this in the past - iirc, Brian Kardell had a good one that he ran through me and Boris Zbarsky.
Authorability
One of the reasons that preprocessors have become so popular in recent years is because they simply make CSS easier to author and maintain. This is extremely valuable when dealing with large, complex sites! I'm already working on this somewhat - CSS Variables exist almost entirely to help with stylesheet maintenance, and the fact that it does lots of other things is a happy accident. Some of my preceding suggestions also help with this, such as mixins and custom functions.
There's more that I'd like to do here, though. First, it seems apparent that scoped stylesheets are a nice win for page maintainability, as they let you apply styles to a subset of a page without having to over-qualify your selectors or worry about your selectors "leaking" and affecting unintended parts of the page. I and fantasai plan to write up a Scoped Stylesheets spec very soon that (a) explains HTML's <style scoped>
attribute properly, and (b) brings an @scoped rule to CSS proper, with the same power.
Another thing that seems really useful now that I've studied it is SASS's @extend rule. Simple uses of @extend are similar to simple uses of @mixin (in fact, they're isomorphic in simple cases), but you can do a lot of nice maintainability hacks with @extend that look really impressive once you see them. For example, if you have a class of things in your application that are all "messages", but some of them are "alerts" or "errors" or "important messages", you'd like them all to receive the common message styling and their specific styling. To do this right now, you have either mint multiple classes, and write your elements like <div class="message alert">
or <div class="message message-alert">
, which feels silly because it's repeating your "inheritance" structure on every instance, or write your selectors such that they target all the alerts and errors and messages at once for the common styling needs, which is also silly because it makes for long selectors and is a maintenance hazard if you ever add more subtypes of messages. @extend lets you deal with this in a clean, reasonable manner, declaring that "alert" and "error" are both types of "message", and then writing selectors that reference "message" and having the styles automatically apply to everyone appropriate. This keeps your HTML clean, and if you ever change the inheritance structure, you have to make the change in one place, rather than in lots of selectors or lots of elements.
Conclusion
Jeez, that ended up long. Let's do a quick summary of what I just wrote, because I can barely remember it all myself:
- Web Animations, a JS API for creating and interacting with CSS/SVG animations.
- Touch-based animation scrubbing, a simple declarative way to have elements on the page respond to touch gestures quickly and smoothly.
- Flexbox and Grid, because they're awesome.
display: viewport
, to make JS-based layouts work a lot better and more performantly.- Polyfillability of various kinds: @mixin, @function, selectors, etc.
- Scoped styles.
- SASS's @extend rule.
Awesome stuffs from 1 to 7. By induction, I guess I also support items 8 to 23 that you forgot to include ;-)
Reply?
It all reads like worthwhile stuff
I wish though there was a way of breaking up and then including CSS files. Yes, there is @import but as you know there are performance issues with this. It would make organising code so much easier rather than having 2000 lines to trawl through
Reply?
Similar to Andy's comment, it feels like there should be an easy way to make scoped styles not block page rendering to reduce the pressure everyone has to compact their entire site into a single file. This is obviously possible using JS but it seems like there could be a standard way to tell the browser that a stylesheet only applies to content which isn't visible yet.
Reply?
In regards to the mentioned scoping (Which is a fantastic idea), nested css rules are still something I'm shooting for. Primarily for my own greedy organization desires.
It'd be nice to see a +1/Upvote option for comments.
Reply?
I'm totally in favor of CSS Nesting (me and a coworker wrote a spec for it), but I'm not happy with the compromises I've had to come up with so far for the parsing issues. I'll figure it out someday, but for now it's not a high priority.
Upvoting comments sounds like a great idea!
Reply?
Super grateful not only for thinking big about some key stuff, but for letting us know what you’re working on.
If I had one bit of input, it’s this: give Mark Boulton a voice on Grid module. He might just be the foremost expert on grid systems for web design, from the designer’s perspective. I believe his voice needs to be heard in the CSSWG and weighted appropriately.
I’m reasonably sure you’re aware of his previous interest and writing, but just in case, here is the gist of what he’s said so far:
http://www.markboulton.co.uk/journal/rethinking-css-grids http://www.markboulton.co.uk/journal/open-letter-to-w3c-css-working-group-re-css-grids
All in all, methinks we’re going to owe you a lot of beers :)
Reply?
Yup, I know quite a bit about Boulton's work. I find it really interesting to hear stuff from his perspective, which is strongly influenced by the print-focused notion of grids - static, fixed grids that the content responds to.
That's in direct opposition to how I think of grids, coming from web design, which is of a fluid grid that responds to the content.
Luckily, it seems reasonably easy to address both of these situations. Grid Layout will be mostly focused on the latter - flexible grids that adjust to content. But we'll then build on another layer, likely using the 'position' property or something, that lets you then position things relative to that grid and respond to it. This'll allow things like "snap to grid", without getting us in difficult-to-resolve layout cycles.
One of Boulton's biggest hobby horses appears to be naming, which I don't think is a big deal. His naming preferences seem totally foreign to me, and I think the current names are easier to understand for most people. The number of people who come to webdev from a strong print background, and who are so committed to the terminology used in print grids that they'll find it confusing to use slightly different names, should be very small.
Reply?