JSFiddle - React, Tailwind, and code Playground

HTML

<script src="http://cdn.ractivejs.org/latest/ractive.js"></script>
<script src="http://ractivejs.github.io/ractive-events-keys/ractive-events-keys.js"></script>
<main></main>

<div id="output" />
<script id='template' type='text/ractive'>
    <form>
        <input on-change='newTodo' on-enter='blur' class='newTodo' placeholder='What needs to be done?'/>
    </form>
<ul class='todos'>
  {{#items:i}}
    {{>item}}
  {{/items}}
</ul>
<h2>To-do list</h2>
<!-- {{>item}} -->
<li data-index='{{i}}' class='{{ done ? "done" : "pending" }}'>
  <input type='checkbox' checked='{{done}}'>
  <span on-tap='edit'>
    {{description}}

    {{#.editing}}
      <input id='editTodo' class='edit' value='{{description}}' on-blur='stop_editing'>
    {{/.editing}}
  </span>
  <a class='button' on-tap='remove'>x</a>
</li>
<!-- {{/item}} -->
</script>

<script id='dropdown-list' type='text-ractive'>
    <select value='{{value}}'>
        {{#options}}
        <option>{{this}}</option>
        {{/options}}
    </select>
</script>

<script id='logger' type='text-ractive'>
    <p>{{message}}</p>
</script>

CSS

body {
    font-family: 'Helvetica Neue', arial, sans-serif;
    font-weight: 200;
    color: #353535;
}

h1, h2, h3, h4, h5, h6 {
    font-weight: 200;
}

JavaScript

Ractive.events.enterkp = Ractive.events.enter;
var TodoList = Ractive.extend({
  template: "#template",

  addItem: function ( description ) {
    this.items.push({
      description: description,
      done: false
    });
  },

  removeItem: function ( index ) {
    this.items.splice( index, 1 );
  },

  editItem: function ( index ) {
    var self = this, keydownHandler, blurHandler, input, currentDescription;

    currentDescription = this.get( 'items.' + index + '.description' );
    this.set( 'items.' + index + '.editing', true );

    input = this.nodes.editTodo;
    input.select();

    window.addEventListener( 'keydown', keydownHandler = function ( event ) {
      switch ( event.which ) {
        case 13: // ENTER
        event.preventDefault();
        input.blur();
        break;

        case 27: // ESCAPE
        input.value = currentDescription;
        self.set( 'items.' + index + '.description', currentDescription );
        input.blur();
        break;

        case 9: // TAB
        event.preventDefault();
        input.blur();
        self.editItem( ( index + 1 ) % self.get( 'items' ).length );
        break;
      }
    });

    input.addEventListener( 'blur', blurHandler = function () {
      window.removeEventListener( 'keydown', keydownHandler );
      input.removeEventListener( 'blur', blurHandler );
    });
    
    this.set( 'items.' + index + '.editing', true );
  },

  init: function ( options ) {
    var self = this;

    this.items = options.items;

    // initialise
    this.set( 'items', this.items );

    // proxy event handlers
    this.on({
      remove: function ( event ) {
        this.removeItem( event.index.i );
      },
      newTodo: function ( event ) {
        this.addItem( event.node.value );
        event.node.value = '';
        setTimeout( function () {
          event.node.focus();
        }, 0 );
      },
      edit: function ( event ) {
        this.editItem( event.index.i );
      },
      stop_editing: function (...