Todo App • HTM + Preact + WindiCSS

Basic todo app example in < 100 lines of code with HTM + Preact + WindiCSS.

by Nicholas Berlette

HTML

<link rel="stylesheet" href="https://unpkg.com/@typehaus/metropolis/100.css">
<link rel="stylesheet" href="https://unpkg.com/@typehaus/metropolis/500.css">
<link rel="stylesheet" href="https://unpkg.com/@typehaus/metropolis/900.css">
<link rel="stylesheet" href="https://unpkg.com/@typehaus/metropolis/700.css">
<div id="app" class="wrapper !opacity-100">
	<noscript>Please enable JavaScript to use this app.</noscript>
</div>

<script type="module">
	import { html, Component, render } from 'https://unpkg.com/[email protected]/preact/standalone.module.js';

	var $ = document.querySelector.bind(document);
	var $$ = document.querySelectorAll.bind(document);
	
	class App extends Component {
		addTodo(input) {
			const { todos = [] } = this.state;
			const text = input.value.trim();
			this.setState({ todos: todos.concat(text.length > 0 ? text : `item number ${(todos.length + 1)}`) });
			input.value = '';
		}
		render({ name = 'Todo List', page = 'today' }, state) {
			return html`
				<div class="app top-rainbow group ${page}">
					<${Header} name="${name || 'Todo'}" page="${page}" />
					<div class="px-2">
						<${List}>
							${(this.state.todos??=[]).map((todo, index) => html`<${Todo} key=${todo + index} id=${`todo-${index}`}>${todo}</li>`)}
						<//>
						<${Input} placeholder="type a todo and press enter to add" onKeyPress=${(e) => (e.keyCode === 13) && this.addTodo($(`.${page} .input-text`))}/>
					</div>
				</div>
			`;
		}
	}

const Header = ({ name, page = 'all' }) => html`<h1 class="h1"><span class="title">${name}</span><span class="page group-hover:!opacity-80">${page}</span></h1>`
const List = props => html`<ul class="todo-list" ...${props} />`
const Input = props => html`<input type="text" class="input-text" ...${props} />`
const Button = props => html`<button class="btn btn-blue text-xs ease-colors" ...${props} />`
const Link = ({ to, text, ...props }) => html`<a href="${to}" title="${text}" class="link" ${to.startsWith('http') ? 'target="_blank"...

CSS

* {
	font-family: 'Metropolis', sans-serif !important;
}
#app {
	transition: 1.5s opacity 1s ease-in;
	opacity: 0;
}