css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <html>
  <head>
    <meta charset="utf-8">
    <link rel="stylesheet" type="text/css" href="style.css">
  </head>
  <body>
    <section class="todoapp">
      <header class="header">
	<h1>todos</h1>
	<input class="new-todo" placeholder="What needs to be done?" autofocus/>
      </header>
      <section class="main">
	<input id="toggle-all" class="toggle-all" type="checkbox">
	<label for="toggle-all">Mark all as complete</label>
	<ul class="todo-list"></ul>
	<footer class="footer">
	  <span class="todo-count"></span>
	  <ul class="filters"></ul>
	  <span id="clearCompleted"></span>
	</footer>
      </section>
      
    </section>
    <footer class="info">
      <p>Double-click to edit a todo</p>
      <p>Written by <a href="http://twitter.com/walter">Walter Higgins</a></p>
    </footer>
  </body>
  <script src="https://rawgit.com/walterhiggins/ickyjs/master/icky.js"
	  type="text/javascript"></script>
  <script src="app.js"
	  type="text/javascript"></script>
</htmL>

            
          
!
            
              html,
body {
	margin: 0;
	padding: 0;
}

button {
	margin: 0;
	padding: 0;
	border: 0;
	background: none;
	font-size: 100%;
	vertical-align: baseline;
	font-family: inherit;
	font-weight: inherit;
	color: inherit;
	-webkit-appearance: none;
	appearance: none;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

body {
	font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
	line-height: 1.4em;
	background: #f5f5f5;
	color: #4d4d4d;
	min-width: 230px;
	max-width: 550px;
	margin: 0 auto;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	font-weight: 300;
}

:focus {
	outline: 0;
}

.hidden {
	display: none;
}

.todoapp {
	background: #fff;
	margin: 130px 0 40px 0;
	position: relative;
	box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
	            0 25px 50px 0 rgba(0, 0, 0, 0.1);
}

.todoapp input::-webkit-input-placeholder {
	font-style: italic;
	font-weight: 300;
	color: #e6e6e6;
}

.todoapp input::-moz-placeholder {
	font-style: italic;
	font-weight: 300;
	color: #e6e6e6;
}

.todoapp input::input-placeholder {
	font-style: italic;
	font-weight: 300;
	color: #e6e6e6;
}

.todoapp h1 {
	position: absolute;
	top: -155px;
	width: 100%;
	font-size: 100px;
	font-weight: 100;
	text-align: center;
	color: rgba(175, 47, 47, 0.15);
	-webkit-text-rendering: optimizeLegibility;
	-moz-text-rendering: optimizeLegibility;
	text-rendering: optimizeLegibility;
}

.new-todo,
.edit {
	position: relative;
	margin: 0;
	width: 100%;
	font-size: 24px;
	font-family: inherit;
	font-weight: inherit;
	line-height: 1.4em;
	border: 0;
	color: inherit;
	padding: 6px;
	border: 1px solid #999;
	box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
	box-sizing: border-box;
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

.new-todo {
	padding: 16px 16px 16px 60px;
	border: none;
	background: rgba(0, 0, 0, 0.003);
	box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03);
}

.main {
	position: relative;
	z-index: 2;
	border-top: 1px solid #e6e6e6;
}

.toggle-all {
	width: 1px;
	height: 1px;
	border: none; /* Mobile Safari */
	opacity: 0;
	position: absolute;
	right: 100%;
	bottom: 100%;
}

.toggle-all + label {
	width: 60px;
	height: 34px;
	font-size: 0;
	position: absolute;
	top: -52px;
	left: -13px;
	-webkit-transform: rotate(90deg);
	transform: rotate(90deg);
}

.toggle-all + label:before {
    content: '❯';
    font-size: 22px;
    color: #e6e6e6;
    padding: 10px 27px 10px 27px;
}

.toggle-all:checked + label:before {
	color: #737373;
}

.todo-list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.todo-list li {
	position: relative;
	font-size: 24px;
	border-bottom: 1px solid #ededed;
}

.todo-list li:last-child {
	border-bottom: none;
}

.todo-list li.editing {
	border-bottom: none;
	padding: 0;
}

.todo-list li.editing .edit {
	display: block;
	width: 506px;
	padding: 12px 16px;
	margin: 0 0 0 43px;
}

.todo-list li.editing .view {
	display: none;
}

.todo-list li .toggle {
	text-align: center;
	width: 40px;
	/* auto, since non-WebKit browsers doesn't support input styling */
	height: auto;
	position: absolute;
	top: 0;
	bottom: 0;
	margin: auto 0;
	border: none; /* Mobile Safari */
	-webkit-appearance: none;
	appearance: none;
}

.todo-list li .toggle {
	opacity: 0;
}

.todo-list li .toggle + label {
	/*
		Firefox requires `#` to be escaped - https://bugzilla.mozilla.org/show_bug.cgi?id=922433
		IE and Edge requires *everything* to be escaped to render, so we do that instead of just the `#` - https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7157459/
	*/
	background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23ededed%22%20stroke-width%3D%223%22/%3E%3C/svg%3E');
	background-repeat: no-repeat;
	background-position: center left;
}

.todo-list li .toggle:checked + label {
	background-image: url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2240%22%20height%3D%2240%22%20viewBox%3D%22-10%20-18%20100%20135%22%3E%3Ccircle%20cx%3D%2250%22%20cy%3D%2250%22%20r%3D%2250%22%20fill%3D%22none%22%20stroke%3D%22%23bddad5%22%20stroke-width%3D%223%22/%3E%3Cpath%20fill%3D%22%235dc2af%22%20d%3D%22M72%2025L42%2071%2027%2056l-4%204%2020%2020%2034-52z%22/%3E%3C/svg%3E');
}

.todo-list li label {
	word-break: break-all;
	padding: 15px 15px 15px 60px;
	display: block;
	line-height: 1.2;
	transition: color 0.4s;
}

.todo-list li.completed label {
	color: #d9d9d9;
	text-decoration: line-through;
}

.todo-list li .destroy {
	display: none;
	position: absolute;
	top: 0;
	right: 10px;
	bottom: 0;
	width: 40px;
	height: 40px;
	margin: auto 0;
	font-size: 30px;
	color: #cc9a9a;
	margin-bottom: 11px;
	transition: color 0.2s ease-out;
}

.todo-list li .destroy:hover {
	color: #af5b5e;
}

.todo-list li .destroy:after {
	content: '×';
}

.todo-list li:hover .destroy {
	display: block;
}

.todo-list li .edit {
	display: none;
}

.todo-list li.editing:last-child {
	margin-bottom: -1px;
}

.footer {
	color: #777;
	padding: 10px 15px;
	height: 20px;
	text-align: center;
	border-top: 1px solid #e6e6e6;
}

.footer:before {
	content: '';
	position: absolute;
	right: 0;
	bottom: 0;
	left: 0;
	height: 50px;
	overflow: hidden;
	box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
	            0 8px 0 -3px #f6f6f6,
	            0 9px 1px -3px rgba(0, 0, 0, 0.2),
	            0 16px 0 -6px #f6f6f6,
	            0 17px 2px -6px rgba(0, 0, 0, 0.2);
}

.todo-count {
	float: left;
	text-align: left;
}

.todo-count strong {
	font-weight: 300;
}

.filters {
	margin: 0;
	padding: 0;
	list-style: none;
	position: absolute;
	right: 0;
	left: 0;
}

.filters li {
	display: inline;
}

.filters li a {
	color: inherit;
	margin: 3px;
	padding: 3px 7px;
	text-decoration: none;
	border: 1px solid transparent;
	border-radius: 3px;
}

.filters li a:hover {
	border-color: rgba(175, 47, 47, 0.1);
}

.filters li a.selected {
	border-color: rgba(175, 47, 47, 0.2);
}

.clear-completed,
html .clear-completed:active {
	float: right;
	position: relative;
	line-height: 20px;
	text-decoration: none;
	cursor: pointer;
}

.clear-completed:hover {
	text-decoration: underline;
}

.info {
	margin: 65px auto 0;
	color: #bfbfbf;
	font-size: 10px;
	text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
	text-align: center;
}

.info p {
	line-height: 1;
}

.info a {
	color: inherit;
	text-decoration: none;
	font-weight: 400;
}

.info a:hover {
	text-decoration: underline;
}

/*
	Hack to remove background from Mobile Safari.
	Can't use it globally since it destroys checkboxes in Firefox
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
	.toggle-all,
	.todo-list li .toggle {
		background: none;
	}

	.todo-list li .toggle {
		height: 40px;
	}
}

@media (max-width: 430px) {
	.footer {
		height: 50px;
	}

	.filters {
		bottom: 10px;
	}
}
            
          
!
            
              //
// This example application is based on the popular ToDoMVC application.
// It is functionally identically (to the best of my knowledge) to the
// ES6 Vanilla version and uses the same CSS stylesheet and initial markup.
//
(function(exports) {
  "use strict";

  // short-hand for icky functions
  let { gnf, update, map } = icky;

  // short-hand for querySelector - default is document.querySelector
  const qs = (selector, el) => (el ? el.querySelector(selector) : document.querySelector(selector));

  // short-hand for subscribing to topics
  const on = (...args) => {
    const callback = args.pop();
    args.forEach(topic => window.addEventListener(topic, callback));
  };
  const publish = (topic, data, el) => {
    let emitter = el ? el : document;
    let event = new CustomEvent(topic, { bubbles: true, detail: data });
    setTimeout(() => emitter.dispatchEvent(event), 0);
  };
  // constants used throughout the rest of the code
  const KEY = {
    ENTER: 13,
    ESCAPE: 27
  };
  // topics used by Model and Controllers
  const TOPIC = {
    ITEMS_LOADED: "todos/itemsLoaded",
    ITEM_CHANGED: "todos/itemChanged",
    ITEM_STATUS_CHANGED: "todos/itemStatusChanged",
    ITEM_REMOVED: "todos/itemRemoved",
    ITEM_ADDED: "todos/itemAdded",
    VISIBILITY_CHANGED: "todos/visibilityChanged",
    BULK_STATUS_CHANGE: "todos/bulkStatusChange"
  };
  const VISIBILITY = {
    ALL: "All",
    ACTIVE: "Active",
    COMPLETED: "Completed"
  };

  // ## Model
  // The Model object is responsible for encapsulating changes to the to-do list.
  // Stored in localStorage's "todos" variable by default.
  function Model(name = "todos") {
    // visibility filter function
    const visible = item => {
      switch (visibility) {
        case VISIBILITY.ALL:
          return true;
        case VISIBILITY.COMPLETED:
          return item.done;
        case VISIBILITY.ACTIVE:
          return !item.done;
      }
    };
    // load app state from localStorage or init if not present.
    let todos = [],
      visibility = VISIBILITY.ALL,
      saved = JSON.parse(localStorage.getItem(name));

    if (saved) {
      todos = saved.todos;
      visibility = saved.visibility;
    }
    // notify listeners that model is ready
    setTimeout(() => publish(TOPIC.ITEMS_LOADED), 0);

    // model is saved to localStorage - save() is private to the model.
    const save = () => {
      localStorage.setItem(name, JSON.stringify({ todos, visibility }));
    };

    // Model's public functions
    return {
      // toggle the status (done) of a to-do item
      // works for both single items and arrays of items.
      toggle: todo => {
        if (todo.constructor == Array) {
          // if it's an array then send a single notification to listeners
          todo.map(item => (item.done = !item.done));
          save();
          publish(TOPIC.BULK_STATUS_CHANGE, todo);
        } else {
          const old = { ...todo };
          todo.done = !todo.done;
          save();
          publish(TOPIC.ITEM_STATUS_CHANGED, { o: old, n: todo });
        }
      },
      // change the text for a to-do item
      setText: (todo, text) => {
        const old = { ...todo };
        todo.text = text;
        save();
        publish(TOPIC.ITEM_CHANGED, { o: old, n: todo });
      },
      // add a new to-do item
      add: todo => {
        let result = todos.push(todo);
        save();
        publish(TOPIC.ITEM_ADDED, result);
      },
      // remove a to-do item
      remove: todo => {
        todos.splice(todos.indexOf(todo), 1);
        save();
        publish(TOPIC.ITEM_REMOVED, todo);
      },
      // change the application's visibility option (All, Active or Completed)
      visibility: v => {
        if (v) {
          visibility = v;
          save();
          publish(TOPIC.VISIBILITY_CHANGED);
        } else {
          return visibility;
        }
      },
      // get a list of visible items
      visible: () => todos.filter(visible),
      // get a list of remaining items
      remaining: () => todos.filter(item => !item.done),
      // get a list of completed items
      completed: () => todos.filter(item => item.done),
      // get a list of all items
      all: () => [...todos]
    };
  }
  // declare model variable used throughout rest of code.
  let model = null;

  // ## Components
  // The to-do list application is composed of distinct components which need
  // to be updated when the user interacts with the app. These components are:
  //
  // 1. The input field for adding new to-do items
  // 2. The list of to-do items each of which have
  //    2.1 A Checkbox to mark the item as completed (this can be toggled on and off)
  //    2.2 A Button to remove the item (appears on hover)
  //    2.3 A Label with the text which when double-clicked becomes an editable input field.
  // 3. A Checkbox to mark ALL items as completed or Active.
  // 4. A Label showing a count of completed items.
  // 5. A List of hyperlinks in the footer which will show either All, Active or Completed items.
  // 6. A Button to Remove all completed items (which only appears if there are >1 completed items)
  //
  //  Many components are constructed by invoking a function which returns a string of HTML.
  //  Constructing strings of HTML is now easier in ES6 thanks to Template Literals.
  //

  // ### Component: Todo List
  const tTodoList = () => {
    //  The todo list can potentially create a lot of DOM elements each with many event handlers.
    //  When using the gnf() function to allocate global names to the event-handler functions, the
    //  functions are referenced from the global window.icky.namespaces.functions object.
    //  To avoid memory leaks construct a new dedicated namespace and naming function which will
    //  be torn-down and reconstructed whenever this function is called.
    let namer = gnf("tTodoList");
    // tTodoList doesn't have any markup itself, it just repeatedly calls tTodoItem.
    return map(model.visible(), item => tTodoItem(namer, item));
  };
  // when any of these messages are received, update the to-do list
  on(
    TOPIC.ITEMS_LOADED,
    TOPIC.BULK_STATUS_CHANGE,
    TOPIC.ITEM_STATUS_CHANGED,
    TOPIC.ITEM_REMOVED,
    TOPIC.ITEM_ADDED,
    TOPIC.VISIBILITY_CHANGED,
    () => update("ul.todo-list", tTodoList)
  );

  // trigger a blur event if the user presses Enter
  const onKeyPressItemEdit = gnf(input => {
    if (event.keyCode == KEY.ENTER) input.blur();
  });
  // trigger a blur event if the user presses Esc
  const onKeyUpItemEdit = gnf(input => {
    if (event.keyCode == KEY.ESCAPE) {
      input.dataset.isCanceled = true;
      input.blur();
    }
  });

  // ### Component: Todo Item.
  // The App's most interactive component. Using this component users can:
  // 1. Toggle the item's status (Completed/Active).
  // 2. Change the item's text
  // 3. Remove the item (by changing the text to an empty string)
  // 4. Remove the item by clicking the Remove button.
  function tTodoItem(nf, todo) {
    // #### Controller code for tTodoItem
    let label, input, listItem;
    // update model if user didn't press Esc
    const onBlurItemEdit = nf(input => {
      listItem.classList.remove("editing");
      if (input.dataset.isCanceled) return;
      var value = input.value.trim();
      if (value.length) {
        // change to-do item's text if length > 0
        model.setText(todo, value);
        label.innerText = value;
      } else {
        // otherwise remove the item (text is '')
        model.remove(todo);
      }
    });
    // go into editing mode when user double-clicks label
    const edit = nf(pLabel => {
      label = pLabel;
      listItem = label.parentElement.parentElement;
      // CSS is used to hide the label and show the input
      listItem.classList.add("editing");
      input = qs("input.edit", listItem);
      input.focus();
    });
    // #### View for tTodoItem.
    // Note the use of ES6 Template Literals.
    return `
    <li class="${todo.done ? "completed" : ""}">
      <div class="view">
        <input class="toggle" type="checkbox" ${todo.done ? "checked" : ""} 
               onchange="${nf(() => model.toggle(todo))}()" />

        <label ondblclick="${edit}(this)">${todo.text}</label>

        <button class="destroy"
                onclick="${nf(() => model.remove(todo))}()"></button>
      </div>
      <input class="edit"
             onblur="${onBlurItemEdit}(this)"
             onkeypress="${onKeyPressItemEdit}(this)" 
             onkeyup="${onKeyUpItemEdit}(this)"
             value="${todo.text}"/>
    </li>`;
  }

  // ### Component: Items remaining
  const tItemsLeft = () => `${model.remaining().length} Items Left`;
  on(
    TOPIC.ITEMS_LOADED,
    TOPIC.BULK_STATUS_CHANGE,
    TOPIC.ITEM_STATUS_CHANGED,
    TOPIC.ITEM_REMOVED,
    TOPIC.ITEM_ADDED,
    () => update(".todo-count", tItemsLeft)
  );

  // ### Component: Clear Completed button
  const onClickClearCompleted = gnf(() => {
    model.completed().forEach(model.remove);
  });
  const tClearCompleted = () => `
  <button class="clear-completed ${model.completed().length ? "" : "hidden"}" 
          onclick="${onClickClearCompleted}()">
    Clear completed
  </button>`;

  on(
    TOPIC.ITEMS_LOADED,
    TOPIC.BULK_STATUS_CHANGE,
    TOPIC.ITEM_STATUS_CHANGED,
    TOPIC.ITEM_REMOVED,
    () => update("#clearCompleted", tClearCompleted)
  );

  // ### Component: Filter links.
  const tFilterList = () => `
    ${tFilterItem("#/", VISIBILITY.ALL)}
    ${tFilterItem("#/active", VISIBILITY.ACTIVE)}
    ${tFilterItem("#/completed", VISIBILITY.COMPLETED)}
  `;
  // ### Component: Filter link
  const tFilterItem = (href, type) => `
  <li>
    <a href="${href}"
       class="${model.visibility() == type ? "selected" : ""}">${type}</a>
  </li>`;
  on(TOPIC.ITEMS_LOADED, TOPIC.VISIBILITY_CHANGED, () => {
    update("ul.filters", tFilterList);
  });

  // ### Component: Toggle-All checkbox
  const toggleAll = qs("input.toggle-all");
  toggleAll.onchange = function() {
    if (this.checked) {
      model.toggle(model.remaining());
    } else {
      model.toggle(model.completed());
    }
  };
  on(TOPIC.ITEMS_LOADED, TOPIC.ITEM_STATUS_CHANGED, TOPIC.ITEM_REMOVED, TOPIC.ITEM_ADDED, () => {
    toggleAll.checked = model.all().length > 0 && model.remaining().length == 0;
  });

  // ### Component: New To-Do input field
  qs("input.new-todo").onchange = function() {
    let text = this.value;
    if (text.trim().length == 0) {
      return;
    }
    model.add({ text: text, done: false });
    this.value = "";
  };

  // ## Routing
  // set up client-side routes for visibility filtering
  const routes = {
    active: () => model.visibility("Active"),
    completed: () => model.visibility("Completed")
  };

  // route based on hash
  const routeByHash = () => {
    let param = location.hash.split("/")[1];
    let action = routes[param] || (() => model.visibility("All"));
    action();
  };
  exports.onhashchange = routeByHash;

  // ## Initialise the App

  // create a new Model object
  model = new Model();
  // update visibility based on location hash
  routeByHash();
})(window);

            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console