My latest Flickr photograph

A few words about Web Components, and why I'm totally in love. in mozilla

Monday 18th of March 2013, 09:10:42 am

For the past week I've been looking at a new, additional take on HTML that is being thought up by a large group of people who think that the web can be further improved. People from Apple, Mozilla, Google, but also general public voices are pitching in to come up with a standard for what they call "web components". Let me come right out and say I think that what they're doing is going to change how we think about web pages, in a good way. I love what they're working on, and with this post (and it's probably the first in a series) I will try to explain what it is they're doing, and why I love the idea so much.

Right now, if you ask someone "in the know" what a web page is, they'll say that it's a combination of HTML, which is used to mark up content on a page, CSS, which is used to define what that marked up content looks like when a browser shows it to you, and JavaScript, which is used to define all the dynamic behaviour a page has (basically "what happens on the page once that page has loaded"), as well as acting as a general programming language to turn web pages more into web "applications". More and more CSS is also getting the ability to define dynamic behaviour, but the kind of behaviour that CSS can do is still about styling the page, whereas JavaScript can also manipulate content (adding, removing, or changing the content, HTML code, and even CSS).

That's an interesting description, because it shows how people who work with the web think about web pages. If you ask the same question to a regular user, someone who has never made a single page, let alone an entire site, and just uses things like Facebook and Youtube and the New York Times, the answer is radically different: a webpage is this "thing", on the web, which let you do things. Watch videos, post messages to your friends, read news articles, that kind of stuff. To a normal user, web pages consist of functional components. The fact that those components are made using HTML, CSS and JavaScript is essentially irrelevant.

So what if you could describe your functional page components as such? What if you wanted to say: "I am the New York Times, and my page layout consists of these blocks. There are a number of article summaries, so 'article summary' is one thing, and there's a number of ad blocks, so 'ad' is another thing, ..." and so on. Right now, whether you're the New York Times or anyone else, you can do that kind of thing using templates. Templates let you say "this is the generic HTML code for something like [an article, an ad block, etc]" and then use that template to build content on your page by taking the generic template, filling in the blanks, and putting that on the page. The problem with this is that there are a million templating engines, and if the New York Times has a good template for articles, there is no way I can just do "view: source" on their webpage and see what their template looks like so I can use it on my site, too.

Another way to think about templates is as "widgets", or "plugins", such as offered by Content Management Systems (CMS) like WordPress, Drupal, Django, Magento, etc. If I have a wordpress site, and my friend has a Django website, and he uses a cool plugin, I can't use that. It's tied into his CMS, and usually has deep server-tie-ins to get its job done. Effectively, these templates, or widgets, or plugins, create lock-in. You can argue "so what", and fair enough, so what, but what if you approached plugins, or widgets, or templates, differently? What if you approached them as *page* components, rather than server functionality? What if these things just lived on the web, like everything else, and you could use them regardless of which CMS or back-end or even front-end technology you used, because they were just standard HTML and CSS and JavaScript?

And to make that "what if" real, Web Components is getting developed. I can rant about this for a few thousand words (and in fact have, my scratchpad for writing this post is very, very long!) but actually showing you what you can do with Web Components is probably going to be much more to your liking, so here we go:

Say I run a project like Mozilla's Popcorn Maker, a website that lets you do video remixing using almost any video on the web. You can mix clips to create your own mashup, or overlay a video of current events over a video of yourself reading the news, with a ticker tape scrolling in the bottom, generally cool stuff. In the current world, I would have a javascript library called Popcorn.js that can take media streams and a collection of plugins, and trigger those plugins based on where in the media stream we are when we play it. 10 seconds in, a text box pops up, 15 seconds in it disappears again. 30 seconds in a google map is shown based on what the people in the video are talking about, at 1 minute 17 it disappears again as the video changes topic.

I can write those plugins as Web Components, and by doing so, make them a resource that others can use on their own page, rather than just on Popcorn Maker. And, conversely, enable others to make similar plugins that they can host on their site, but I would be able to use in Popcorn Maker very simply. I'd do this by defining each of my Popcorn plugins as a normal HTML element that encodes my plugin properties (its start time, its end time, position on the page when it's shown, etc) using normal HTML markup, for instance:

  <div class="my-popcorn-plugin">
    <time class="start">0:10</time>
    <time class="end">0:15</time>
    <p>This box pops up at 10s and disappears at 15s!</p>
  </div>

and I'd make a components.css file that has this CSS rule:

  .my-popcorn-plugin {
    decorate: url(#my-popcorn-plugin-component);
  }

That's a new CSS property that is used in combination with Web Components. It says "take this element, and decorate it based on the an HTML element that's also on this page with id 'my-popcorn-plugin-component'", where that HTML element is a special element of type <decorator>. If you're not familiar with decorators from a programming context, decorators are things that "wrap themselves around" something, preserving the information in that something, but making it bigger, better, more functional and perhaps even more beautiful. In the case of Web Components, we're aiming for all four. To stick with the example, I will show you the decorate I'd use for my popcorn plugin:

  <decorator id="my-popcorn-plugin-component">
    <script>
      if(!Popcorn) {
        syncFetchAndInject(popcorn.js);
      }
      Popcorn.registerAsNewPlugin(this);
    </script>
    <template>
      <div class="popcorn-plugin popcorn-web-component">
        <content></content>
      </div>
    </template>
  </decorator>

What does this do? first off when the CSS rule styles my <div cass="my-popcorn-plugin">, the browser does some DOM manipulations: first, it takes the content from my div, and puts it inside the <content> element in the template, then replaces my div with this filled-in template:

  <div class="my-popcorn-plugin">
    <time class="start">0:10</time>
    <time class="end">0:15</time>
    <p>This box pops up at 10s and disappears at 15s!</p>
  </div>
  <div class="popcorn-plugin popcorn-web-component">
    <time class="start">0:10</time>
    <time class="end">0:15</time>
    <p>This box pops up at 10s and disappears at 15s!</p>
  </div>

If that's all it did, this would be fairly silly, it looks just like an outerHTML replacement, but it does more than just this template replacement. When it does the replacement it will also execute the script block that it finds for this decorator. I told it that it should check for popcorn.js, and if that hasn't been loaded, fetch and load it (turned into a single function call for legibility, here), and then register this element as being a plugin definition with popcorn. The best part is that I can put these things in their own file, let's called it popcornComponents.html:

  <style>
    .popcorn-plugin { ... }
    .popcorn-web-component { ... }
    .decorated-for-popcorn {
      decorate: url(attr('data-plugin-type'));
    }
  <style>
    ↓
  <div class="decorators">
    <decorator id="popcorn-video-block">
      <script>...</script>
      <template>
        <div class="popcorn-plugin popcorn-video">
          ...
        </div>
      </template>
    </decorator>
    <decorator id="popcorn-text-plugin"> ... </decorator>
    <decorator id="popcorn-wikipedia-plugin"> ... </decorator>
    <decorator id="popcorn-google-maps-plugin"> ... </decorator>
    ...
  </div>

And I put this file up on http://components.webmaker.org/popcornComponents.html - now anyone can use my components without even copy-pasting them: they just add a link in their page header just like for CSS, except with the "component" relation, and they're good to go:
<link rel="components" href="http://components.webmaker.org/popcornComponents.html">

They now have everything they need to build their own "Popcorn remix". A modicum of HTML knowledge is still required, but the work involved becomes infinitely less. I could now put a remix on my own website without ever having to touch Popcorn Maker, or JavaScript:

  <html>
    <head>
      <title>lol, awesome mashup</title>
      <link rel="components" href="https://components.webmaker.org/popcornComponents.html">
      <link rel="components" href="https://components.google.com/fancyHeader.html">
      <link rel="components" href="https://api.fb.com/components/like.html">
      <link rel="components" href="https://api.fb.com/components/comments.html">
    </head>
    <body>
      <div class="google-fancy-header">
        <p>This is my bestest mashup!</p>
      </div>
      <div class="decorated-for-popcorn" data-plugin-type="popcorn-text-plugin">
        <time class="start">0:10</time>
        <time class="end">0:15</time>
        <p>This box pops up at 10s and disappears at 15s!</p>
      </div>
      <div class="decorated-for-popcorn" data-plugin-type="popcorn-text-plugin">
        <time class="start">0:15</time>
        <time class="end">0:30</time>
        <p>And then this box pops up after that, for another 15 seconds!</p>
      </div>
      <div class="decorated-for-popcorn" data-plugin-type="popcorn-wikipedia-plugin">
        <time class="start">0:15</time>
        <time class="end">0:30</time>
        <data class="pageUrl">en.wikipedia.org/wiki/I18N</data>
      </div>
      <div class="decorated-for-popcorn" data-plugin-type="popcorn-credit-roll-plugin">
        <p>Video: the National Zoo</p>
        <p>Music: Skrillex</p>
        <p>Mashup: ME!</p>
        <p>catering: Tim Hortons</p>
      </div>
      <div class="fb-likes"></div>
      <div class="fb-comments"></div>
    </body>
  </html>

My friend sees my mashup page, and loves it. They want to do something similar. They hit view:source, copy and paste the components <link>, and to get them start, copy-paste my popcorn elements, and they're off, they immediately have a working page that they can play around with themselves. No going to a special website, no loading specialised tools, copy paste and off you go!

But it doesn't end there. As Popcorn Maker, the actual editor components can also be Web Components. For instance, every plugin has a start and end time, what if the timeline component was simply a decorator that, when actually applied on a page, gives you a timeline bar at the bottom of your page (anchored to the browser window), and searches for everything with the class "popcorn-plugin", adding those things as timeline events so you can see what's going on? Easy-peasy, and if someone wanted to play with their page like that, they'd just have to add the component:

  ...
  <div id="timeline" class="decorated-for-popcorn" data-plugin-type="popcorn-maker-timeline-ui"></div>
  ...

Done? Delete that single div and it's gone. Need it back? Put it back in and magically the timeline view is back. This is webmaking at its finest. "How do I add a facebook comment section to my page?" - "oh just use the facebook components and add an 'fb-comments' div wherever you want the comments to sit". "How do I put a code editor on my page?" - "link to the codemirror components and then just add a class='codemirror' div".

"How do I add X?" - "by adding X".

This is a future I want to live in!