<svg style="display: none">
  <symbol id="bin-icon" height="50" width="50" viewBox="0 0 50 50">
   <path style="fill:#000" d="m20.651 2.3339c-.73869 0-1.3312.59326-1.3312 1.3296v2.5177h-6.3634c-.73887 0-1.3314.59331-1.3314 1.3295v1.1888c0 .73639.59249 1.3289 1.3312 1.3289h7.6948 8.8798 7.6948c.73869 0 1.3312-.59249 1.3312-1.3289v-1.1888c0-.73639-.59249-1.3296-1.3312-1.3296h-6.3634v-2.5177c0-.73639-.59249-1.3296-1.3312-1.3296h-8.8798zm-5.6786 11.897c-1.7775 0-3.2704 1.4889-3.2704 3.274v27.647c0 1.7775 1.4928 3.2704 3.2704 3.2704h20.783c1.7775 0 3.2704-1.4928 3.2704-3.2704v-27.647c0-1.7852-1.4928-3.274-3.2704-3.274h-20.783zm1.839 3.4895h1.1696c.73869 0 1.3389.60018 1.3389 1.3466v24.247c0 .74638-.60018 1.3389-1.3389 1.3389h-1.1696c-.73869 0-1.3389-.59249-1.3389-1.3389v-24.247c0-.74638.60018-1.3466 1.3389-1.3466zm7.6948 0h1.1696c.73869 0 1.3389.60018 1.3389 1.3466v24.247c0 .74638-.60018 1.3389-1.3389 1.3389h-1.1696c-.73869 0-1.3389-.59249-1.3389-1.3389v-24.247c0-.74638.60018-1.3466 1.3389-1.3466zm7.6948 0h1.1696c.73869 0 1.3389.60018 1.3389 1.3466v24.247c0 .74638-.60018 1.3389-1.3389 1.3389h-1.1696c-.73869 0-1.3389-.59249-1.3389-1.3389v-24.247c0-.74638.60018-1.3466 1.3389-1.3466z"/>
  </symbol>
</svg>

<section id="component" aria-labelledby="todos-label">
  <p class="smaller">This is a demo from <a href="https://inclusive-components.design/a-todo-list/">A Todo List</a>, published on <strong>Inclusive Components</strong>.</p> 
  <h1 id="todos-label" tabindex="-1">My TODO List</h1>
  <ul>
    <li v-for="(todo, index) in todos">
      <input type="checkbox" :id="`todo-${index}`" v-model="todo.done" class="vh">
      <label :for="`todo-${index}`">
        <span class="tick"></span>
        <span class="text">{{todo.name}}</span>
      </label>
      <button @click="remove(index, todo.name)" :aria-label="`delete ${todo.name}`">
        <svg><use xlink:href="#bin-icon"></use></svg>
      </button>
    </li>
  </ul>
  <div class="empty-state">
    <p>Either you've done everything already or there are still things to add to your list. Add your first todo &#x2193;</p>
  </div>
  <form @submit="add">
    <label for="add" class="vh">
      Write a new todo item
    </label>
    <input type="text" id="add" v-model="workingName" placeholder="E.g. Adopt an owl" :aria-invalid="validity">
    <button type="submit" :disabled="validity">Add</button>
  </form>
  <div role="status" class="vh">
    {{feedback}}
  </div>
</section>
@import url('https://fonts.googleapis.com/css?family=Asap');

* {
  font-size: inherit;
  font-family: inherit;
}

html {
  font-size: calc(1em + 1vw);
  font-family: Asap, sans-serif;
}

body {
  max-width: 20rem;
  margin: 0;
  padding: 1rem;
}

h1 {
  margin-top: 0;
  font-size: 1.25rem;
}

ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

ul:empty, .empty-state {
  display: none;
}

ul:empty + .empty-state {
  display: block;
}

li {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tick {
  display: inline-block;
  width: 0.66rem;
  height: 0.66rem;
  border: 0.125rem solid;
  margin-right: 0.25rem;
  border-radius: 0.125rem;
}

[type="checkbox"]:checked +  label .tick {
  background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgaGVpZ2h0PSI1MCIgd2lkdGg9IjUwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgdmlld0JveD0iMCAwIDUwLjAwMDAwMSA1MC4wMDAwMDEiPiA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIC0xMDAyLjQpIj4gIDxyZWN0IHN0eWxlPSJzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlOiMxYTFhMWE7c3Ryb2tlLXdpZHRoOjMuNDQ1MztzdHJva2UtbGluZWNhcDpyb3VuZDtmaWxsOiMxYTFhMWEiIHRyYW5zZm9ybT0ibWF0cml4KC44ODc1OSAuNDYwNjQgLS40NTEyNyAuODkyMzkgMCAwKSIgcnk9IjEuMTUwNCIgaGVpZ2h0PSIzMS42OTEiIHdpZHRoPSI1Ljk5MyIgeT0iODgyLjcxIiB4PSI0ODcuMTkiLz4gIDxyZWN0IHN0eWxlPSJzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlOiMxYTFhMWE7c3Ryb2tlLXdpZHRoOjMuMTM5MjtzdHJva2UtbGluZWNhcDpyb3VuZDtmaWxsOiMxYTFhMWEiIHRyYW5zZm9ybT0icm90YXRlKDI3LjA4NSkiIHJ5PSIuOTExNjkiIGhlaWdodD0iNi4yNzgyIiB3aWR0aD0iMTguODM1IiB5PSI5MTEuMjIiIHg9IjQ3OC42MiIvPiA8L2c+PC9zdmc+);
  background-repeat: none;
  background-position: center;
  background-size: 100%;
}

[type="checkbox"]:checked + label .text {
  text-decoration: line-through;
}

li label {
  flex: 2;
}

li + li {
  margin-top: 0.55rem;
}

button {
  margin: 0;
  line-height: 1;
  border: 0;
}

li button {
  border: 0;
  padding: 0;
  background: 0;
}

button svg {
  width: 1.125em;
  height: 1.125em;
}

form {
  margin-top: 1rem;
  display: flex;
}

input, [type="submit"] {
  border: 0.125rem solid;
  border-radius: 0.125rem;
  line-height: 1;
}

[type="text"] {
  flex: 3;
  margin-right: 0.25rem;
}

[type="submit"] {
  background: #000;
  color: #fff;
  padding: 0.25rem 0.5rem;
  border: 2px solid #000;
}

[type="submit"][disabled] {
  opacity: 0.33;
}

::-webkit-input-placeholder { 
  color: #444;
  font-style: italic;
}
::-moz-placeholder { 
  color: #444;
  font-style: italic;
}
:-ms-input-placeholder { 
  color: #444;
  font-style: italic;
}
:-moz-placeholder { 
  color: #444;
  font-style: italic;
}

input:focus, button:focus, [type="checkbox"]:focus + label .tick {
  outline: none;
  box-shadow: 0 0 0 0.125rem #2a7fff;
}

[tabindex="-1"]:focus {
  outline: none;
}

.vh {
  position: absolute !important;
  clip: rect(1px, 1px, 1px, 1px);
  padding:0 !important;
  border:0 !important;
  height: 1px !important;
  width: 1px !important;
  overflow: hidden;
}

.smaller {
  font-size: 0.75rem;
}
var app = new Vue({
  el: '#component',
  data: {
    todos: [
     {
       name: 'Pick up kids from school',
       done: true
     },
     {
       name: 'Learn Haskell',
       done: false
     },
     {
       name: 'Sleep',
       done: false
     }
    ],
    workingName: '',
    feedback: ''
  },
  methods: {
    add(e) {
      e.preventDefault();
      this.todos.push({
        name: this.workingName,
        done: false
      });
      this.feedback = `${this.workingName} added`;
      this.workingName = '';
    },
    remove(index, name) {
      this.todos.splice(index, 1);
      document.getElementById('todos-label').focus();
      this.feedback = `${name} deleted`;
    }
  },
  computed: {
    validity() {
      return this.workingName.trim() === '';
    }
  }
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js