Skip to content

Instantly share code, notes, and snippets.

@bkardell
Last active August 29, 2015 13:56
Show Gist options
  • Save bkardell/8976713 to your computer and use it in GitHub Desktop.
Save bkardell/8976713 to your computer and use it in GitHub Desktop.
Progressive rendering facilitation

#Our Asynchronous Past One of the things that made the early Web interesting was the fact that the page could render as it was downloading. When we introduced images, design wasn't much of a thing on the web, so we made them async. The browser rendered a placeholder and when the image downloaded it would pop into existence. This was great in some respects because authors could get the (then) majority text content much more quickly so a user can start reading, and on the low banwidth connections of the past this was a pretty huge deal.

Still, a page isn't exactly readable if the text is radically jumping around. To combat this, we simply made sure that the placeholder itself could be appropriately sized and the screen simply 'painted' that part later. This wasn't much of a problem because typically images play a pretty important design element role, so the page would know how big it should be.

#Our Unhappy Middle As the Web started to gain capabilities and grow in new and originally unexpected ways we've increasingly found ourseves battling the jumping content problem, which gained the commonly reviled name "FOUC" (flash of unstyled content). Simultaneusly, we have similar desires - we want to get users something useful as soon as possible.

Progressive Enhancement, for example, is a great idea but it means that we can't show that content (or potentially things after it) until such a point as we can avoid FOUC. Have some javascript that generates DOM? This can be a problem unless you can stick a box around it. When you get into 'sharable' components (including Web Components) sometimes we can't honestly know exactly how big something will be -- sometimes not even until we make some additional data requests. To this end, as currently designed, HTML Imports blocks rendering until the import is complete. An import is only complete once that file has been requested and any linked dependencies are complete (and any dependendices they have are complete) and so on.

But it's not the only case where standards have bumped up against this limitation: Flexbox is super handy, but flexing requires knowing content that isn't necessarily known as soon as an element is read. In fact, just about anything that deals with layout in CSS in a reasonably flexible way will run into similar problems. CSS also has some selectors like this: :last-child, for example is conceptually true for each element as it is parsed until the final element is read. Very useful selectors like :has() would imply similar issues. What to do in any case isn't cut and dry, and if you don't like the answer that comes out of the box, you're forced to hack around the problem. Fortunately, we've gotten fairly adept at it, but it shows no real sign of letting up, and keep in mind that mobile can be slow, so these problems are greatly exacerbated.

So what if we exposed some state on the DOM itself, document this in CSS with default styles? For example:

:domcontentloaded { display: auto; /* this is default */ }

If a browsers handling of last-child was causing you issues and you decided to hide all children explicitly until it was fully parsed, you could just say:

.foo:not(:domcontentloaded) > * { display: none; }

Likewise we could add a corresponding :loaded which would apply to any resources whose contained resources had been completely loaded.

html:not(:loaded) .finishedLoadingIndicator { visibility: hidden; }

In terms of web components, they have a concept called :resolved. Let's imagine that this means that the required definition (including assets) are fully loaded. So what if we made loading async, like images, with similar behavior, and even use that to explain the <img> tag's behavior. An <img> tag has is in the :not(:resolved) state until its required assets are loaded. Like the <img> tag, your custom elements would serve as a placeholder with the same kinds of FOUC issues. Since many custom elements play a similar design role with images, this is fine, we can handle it the same way. Take lessons we learned in that space and apply them here.

If we take this just one step further and, as things are resolved, they are added to a named collection accessible via a pseudo-element on html, we can pick and choose intelligently in cases where this isn't so. Because Component Resolution (like content being parsed or loaded) is a one-time event, we can do this with deadly efficiency. For example, if I had a bar which contained social media buttons provided by other players. I might want to reserve the space for the bar but not actually display any of them until they are ready - I don't want those buttons jumping around. I don't want to start to click 'like' and then have the twitter button spring into existence and mess with my click.

html:not(resolved(facebook-like, twitter-tweet, google-plus)) .social > *{ display: none; }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment