What Next?

Alex Russell
FOWA
Oct 4, 2011

Frameworks & Compilers Are Plan B

Say what you must
Say what you mean

Adding It Up

http://www.flickr.com/photos/melbournewater/5834281031

What if...

http://www.flickr.com/photos/lodefink/891416169

CSS Variables

.themedButton {
  border: 1px solid #7a7;
  background: -webkit-linear-gradient(top, #9d9, #7a7);
}
.themedButton:active, .themedButton.toggled {
  background: -webkit-linear-gradient(top, bottom, #585, #7a7);
}
@var $base #585;
@var $light #7a7;
@var $veryLight #9d9;

.themedButton {
  border: 1px solid $themeLight;
  background: -webkit-linear-gradient(top, bottom, $veryLight, $light);
}
.themedButton:active, .themedButton.toggled {
  background: -webkit-linear-gradient(top, bottom, $base, $light);
}

CSS Mixins

@var $base #585;
@var $light #7a7;
@var $veryLight #9d9;

@trait backgroundLinearGradient($from, $to) {
  background: -moz-linear-gradient(top, $from, $to);
  background: -webkit-linear-gradient(top, bottom, $from, $to);
}

.themedButton {
  border: 1px solid $themeLight;
  @mixin backgroundLinearGradient($veryLight, $light);
  @mixin otherTrait;
}
.themedButton:active, .themedButton.toggled {
  @mixin backgroundLinearGradient($base, $light);
}

CSS Nesting/Hierarchy — Before

.slide.image {
  background-repeat: no-repeat;
  background-size: cover;
  background-position: top left;
}
.slide.image .credit                { /*...*/ }
.slide.image .counter               { /*...*/ }
.slide.image section                { /*...*/ }
.slide.image section h1,
.slide.image section h2             { /*...*/ }
.slide.image section.bottomLeft     { /*...*/ }
.slide.image section.bottomRight    { /*...*/ }
.slide.image section.topLeft        { /*...*/ }
.slide.image section.topRight       { /*...*/ }
.slide.image section.topWide        { /*...*/ }
.slide.image section.bottomWide     { /*...*/ }
        

CSS Nesting/Hierarchy — After

.slide.image {
  background-repeat: no-repeat;
  background-size: cover;
  background-position: top left;

  & .credit     { /*...*/ }
  & .counter    { /*...*/ }
  & section     {
    & h1, & h2      { /*...*/ }
    &.bottomLeft    { /*...*/ }
    &.bottomRight   { /*...*/ }
    &.topLeft       { /*...*/ }
    &.topRight      { /*...*/ }
    &.topWide       { /*...*/ }
    &.bottomWide    { /*...*/ }
  }
}

Current transitions

p { color: blue; transition: color .2s; }
p:hover { color: red; }
      

Current animations

button:hover { animation: pulse-color .5s infinite; }
button:active { animation: fancy-press .1s; }
      

Better animations

button { state-interaction: normal; }
button:active { state-interaction: pressed; }

@transition button {
  over: state-interaction;
  from: normal;
  to: pressed;
  animation: fancy-press .1s;
  interrupt: reverse;
}
/* And a similar @transition from ‘pressed’ back to ‘normal’ */
        

Calculation Today

<script src="dojo.js"></script> 
<script> 
  var subAndApply = (function(){
    var ctr = 0;
    return function(node){
      var name = "doit"+(ctr++);
      var style = dojo.replace(
        dojo.byId("styles").value,
        { doit: name,
          start: "0px",
          end: "100px", },
        /\$\{([^\}]+)\}/g
      );
      var s = dojo.create("style", null, dojo.query("head")[0]);
      s.appendChild(document.createTextNode(style));
      dojo.query(node).addClass(name);
    }
  })();
  dojo.ready(function(){ subAndApply("#foo"); });
</script> 
<textarea id="styles"> 
  .${doit} {
    -webkit-animation-name: ${doit};
    -webkit-animation-duration: 5s;
  }

  @-webkit-keyframes ${doit} {
    0% {
      top: ${start};
    }

    100% {
      top: ${end};
    }
  }
</textarea> 
...
        

Calculations In The System

@transition div.alert {
  over: left;
  animation: fly-with-bounce 0.5s;
  interrupt: restart;
}

@keyframes fly-with-bounce {
  0% { left: from(); }
  90% { left: calc(to() + 10%); }
  100% { left: to(); }
}
        

Scoped CSS

<div class="commentTemplate">
  <style scoped>
    img { border: 1px solid black; margin-bottom: 1em; float: left; }
    .text { margin-left: 71px; }
    .holder { clear: left; margin-bottom: 1em; }
  </style>
  <img class="avatar" alt="has a border">
  <div class="text"></div>
</div>

<img alt="not affected by scoped rules!">
        

Model-Driven Views

<template iterate="items" id="t"> 
  <li>{{name}}
    <ul><template ref="t" iterate="children"></template></ul> 
  </li> 
</template> 
<script> 
document.body.model = Model.get({
  'items': [
    { 'name': 'Africa',
      'children': [
        { 'name': 'Egypt' },
        { 'name': 'Kenya',
          'children': [ { 'name': 'Nairobi' }, { 'name': 'Mombasa' } ]
    } ] } ]
});
</script>

Web Components

function Comment(text) {
  HTMLElement.call(this); // Makes this an Element
  this.textContent = text || '...';
  this.buildUI();
}

Comment.prototype = Object.create(HTMLElement.prototype);
Comment.prototype.constructor = Comment;
Comment.prototype.buildUI = function() { ... };

HTMLElement.register('x-comment', Comment);
<x-comment>...</x-comment>

Shadow DOM

function Comment(text) {
  HTMLElement.call(this); // Makes this an Element
  this.textContent = text || '...';
  this.shadow = new ShadowRoot(this);
  this.buildUI();
}

Comment.prototype = Object.create(HTMLElement.prototype);
Comment.prototype.constructor = Comment;
Comment.prototype.buildUI = function() { ... };

HTMLElement.register('x-comment', Comment);
<template id="commentTemplate">
  <div class="holder">
    <style scoped>
      img     { ... }
      .text   { ... }
      .holder { ... }
    </style>
    <img class="avatar" alt="avatar">
    <div class="text">
      <content></content>
    </div>
  </div>
</template>

Classes Today

function Comment(text) {
  HTMLElement.call(this); // Makes this an Element
  ...
}

Comment.prototype = Object.create(HTMLElement.prototype);
Comment.prototype.constructor = Comment;
Comment.prototype.buildUI = function() { ... };

HTMLElement.register('x-comment', Comment);

ES.next Classes

class Comment extends HTMLElement {

  constructor(attrs = {}) {
    super(attrs);
    this.textContent = attrs.text || lorem;
    this.shadow = new ShadowRoot(this);
    this.buildUI();
  }

  buildUI() { ... }
}

HTMLElement.register('x-comment', Comment);

Modules Today

(function() {
  function Comment(text) {
    HTMLElement.call(this); // Makes this an Element
    ...
  }

  Comment.prototype = Object.create(HTMLElement.prototype);
  Comment.prototype.constructor = Comment;
  Comment.prototype.buildUI = function() { ... };

  HTMLElement.register('x-comment', Comment);

})();
toolkit.require("widgets.Comment");

Modules Today (More Realistic)

define("dijit/Tree",
  ["dojo",
   "dijit",
   "text!dijit/templates/TreeNode.html",
   "text!dijit/templates/Tree.html",
   "dojo/fx",
   "dojo/DeferredList",
   "dijit/_Widget",
   "dijit/_TemplatedMixin",
   "dijit/_Container",
   "dijit/_Contained",
   "dijit/_CssStateMixin",
   "dojo/cookie",
   "dijit/tree/TreeStoreModel",
   "dijit/tree/ForestStoreModel",
   "dijit/tree/_dndSelector"], function(dojo, dijit) {
  ...
  return dijit.Tree;
});

ES.next Modules

module widgets {
  export class Comment extends HTMLElement {
    constructor(attrs = {}) {
      ...
    }
    buildUI() { ... }
  }
  export function factory(type) { ... }

  HTMLElement.register('x-comment', Comment);
}
import {Comment, factory} from widgets;

Prototypes

http://code.google.com/p/traceur-compiler

http://code.google.com/p/experimental-css

http://code.google.com/p/mdv

http://www.flickr.com/photos/mikechen-metalman/4394618656

Thank you!

Questions?

@slightlylate

slightlyoff@chromium.org