Guía clara y paso a paso para crear una pequeña app de tareas: planifica las funciones, crea las pantallas, añade la lógica, guarda los datos, prueba y publica.

Cuando digo “app” en esta guía, me refiero a una pequeña aplicación web: una sola página que abres en un navegador y que responde a lo que haces al hacer clic y escribir. Sin instalaciones, sin cuentas, sin configuraciones pesadas: solo un proyecto sencillo que puedes ejecutar localmente.
Al terminar, tendrás una app de tareas que puede:
localStorage (para que cerrar la pestaña no borre todo)No será perfecta ni “para empresas”, y esa es la idea. Es un proyecto para principiantes diseñado para enseñar lo básico sin lanzar muchas herramientas al mismo tiempo.
Construirás la app paso a paso y aprenderás las piezas centrales de cómo funcionan las aplicaciones front-end:
Manténlo simple. Solo necesitas:
Si puedes crear una carpeta y editar algunos archivos, estás listo.
Antes de escribir código, decide cómo se verá el “éxito”. Este tutorial construye una app pequeña con una sola función clara: ayudarte a seguir tareas que quieres hacer.
Escribe una frase que puedas tener presente mientras construyes:
“Esta app me permite añadir tareas a una lista para no olvidarlas.”
Eso es todo. Si te sientes tentado a añadir calendarios, recordatorios, etiquetas o cuentas, deja esas ideas para más adelante.
Haz dos listas rápidas:
Imprescindible (para este proyecto):
localStorage más adelante)Opcional (no requerido hoy): fechas de vencimiento, prioridades, categorías, búsqueda, arrastrar y soltar, sincronización en la nube.
Mantener lo “imprescindible” pequeño te ayuda a terminar realmente.
Esta app puede ser una sola página con:
Sé específico para no atascarte:
Con eso decidido, estás listo para configurar los archivos del proyecto.
Antes de escribir código, crea un pequeño “hogar” limpio para la app. Mantener los archivos organizados desde el inicio hace que los siguientes pasos sean más fluidos.
Haz una nueva carpeta en tu ordenador y nómbrala algo como todo-app. Esta carpeta contendrá todo el proyecto.
Dentro de esa carpeta, crea tres archivos:
index.html (la estructura de la página)styles.css (el aspecto y diseño)app.js (el comportamiento y la interactividad)Si tu ordenador oculta las extensiones de archivo (p. ej. “.html”), asegúrate de crear realmente los archivos. Un error común es acabar con index.html.txt.
Abre la carpeta todo-app en tu editor de código (VS Code, Sublime Text, etc.). Luego abre index.html en tu navegador.
En este punto, la página puede estar en blanco—y está bien. Añadiremos contenido en el siguiente paso.
Cuando edites tus archivos, el navegador no se actualizará automáticamente (a menos que uses una herramienta que lo haga).
Así que el ciclo básico es:
Si algo “no funciona”, refrescar es lo primero que debes intentar.
Puedes construir esta app haciendo doble clic en index.html, pero un servidor local puede evitar problemas raros más adelante (especialmente cuando empieces a guardar datos o cargar archivos).
Opciones fáciles para principiantes:
python -m http.server
Luego abre la dirección que te muestre (a menudo http://localhost:8000) en tu navegador.
Ahora crearemos un esqueleto limpio para la app. Este HTML no hará nada interactivo aún (eso viene después), pero le da a tu JavaScript lugares claros para leer y escribir.
Incluiremos:
Usa nombres simples y legibles. IDs y clases claros facilitan los siguientes pasos porque tu JavaScript puede seleccionar elementos por nombre sin confusiones.
index.html<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>To‑Do App</title>
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<main class="app" id="app">
<h1 class="app__title" id="appTitle">My To‑Do List</h1>
<form class="task-form" id="taskForm">
<label class="task-form__label" for="taskInput">New task</label>
<div class="task-form__row">
<input
id="taskInput"
class="task-form__input"
type="text"
placeholder="e.g., Buy milk"
autocomplete="off"
/>
<button id="addButton" class="task-form__button" type="submit">
Add
</button>
</div>
</form>
<ul class="task-list" id="taskList" aria-label="Task list"></ul>
</main>
<script src="app.js"></script>
</body>
</html>
Eso es todo por la estructura. Observa que usamos id="taskInput" e id="taskList": esos serán los dos elementos con los que hablará más tu JavaScript.
Ahora mismo la página existe, pero probablemente se vea como un documento simple. Un poco de CSS la hace más fácil de usar: espaciado claro, texto legible y botones que parecen clicables.
Una caja centrada mantiene la app enfocada y evita que el contenido se estire en pantallas anchas.
/* Basic page setup */
body {
font-family: Arial, sans-serif;
background: #f6f7fb;
margin: 0;
padding: 24px;
}
/* Centered app container */
.container {
max-width: 520px;
margin: 0 auto;
background: #ffffff;
padding: 16px;
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
}
Cada tarea debería parecer una fila separada, con espaciado cómodo.
ul { list-style: none; padding: 0; margin: 16px 0 0; }
li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 12px;
border: 1px solid #e7e7ee;
border-radius: 8px;
margin-bottom: 10px;
}
.task-text { flex: 1; }
Cuando una tarea esté completada, debería cambiar visualmente para que se note de un vistazo.
.done .task-text {
text-decoration: line-through;
color: #777;
opacity: 0.85;
}
Que los botones tengan el mismo tamaño y estilo para que parezcan parte de una misma app.
button {
border: none;
border-radius: 8px;
padding: 8px 10px;
cursor: pointer;
}
button:hover { filter: brightness(0.95); }
Eso es suficiente estilo para una interfaz limpia y agradable—sin trucos avanzados. A continuación conectaremos el comportamiento con JavaScript.
Ahora que tienes el campo de entrada, el botón y la lista en la página, harás que hagan algo. El objetivo es simple: cuando alguien escribe una tarea y pulsa Añadir (o presiona Enter), debe aparecer un elemento nuevo en la lista.
En tu archivo JavaScript, primero toma los elementos que necesitas, luego escucha dos acciones: un clic del botón y la tecla Enter dentro del input.
const taskInput = document.querySelector('#taskInput');
const addBtn = document.querySelector('#addBtn');
const taskList = document.querySelector('#taskList');
function addTask() {
const text = taskInput.value.trim();
// Block empty tasks (including ones that are just spaces)
if (text === '') return;
const li = document.createElement('li');
li.textContent = text;
taskList.appendChild(li);
// Clear the input and put the cursor back
taskInput.value = '';
taskInput.focus();
}
addBtn.addEventListener('click', addTask);
taskInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter') {
addTask();
}
});
trim() para quitar espacios extra al inicio/final.li, le asigna el texto y lo añade a la lista.Si no pasa nada, revisa que los IDs en el HTML coincidan exactamente con los selectores en JavaScript (es uno de los fallos más comunes para principiantes).
Ahora que puedes añadir tareas, hagámoslas accionables: deberías poder marcar una tarea como hecha y eliminarla.
En lugar de guardar las tareas como cadenas, guárdalas como objetos. Eso les da una identidad estable y un lugar para rastrear el estado “hecho”:
text: lo que dice la tareadone: true o falseid: un número único para poder encontrar/borrar la tarea correctaAquí hay un ejemplo simple:
let tasks = [
{ id: 1, text: "Buy milk", done: false },
{ id: 2, text: "Email Sam", done: true }
];
Cuando renders cada tarea en la página, incluye un checkbox o un botón “Hecho”, además de un botón “Borrar”.
Un patrón amigable para principiantes es delegación de eventos: pon un listener de clic en el contenedor de la lista y luego comprueba qué se ha pulsado.
function toggleDone(id) {
tasks = tasks.map(t => t.id === id ? { ...t, done: !t.done } : t);
renderTasks();
}
function deleteTask(id) {
tasks = tasks.filter(t => t.id !== id);
renderTasks();
}
document.querySelector("#taskList").addEventListener("click", (e) => {
const id = Number(e.target.dataset.id);
if (e.target.matches(".toggle")) toggleDone(id);
if (e.target.matches(".delete")) deleteTask(id);
});
En tu función renderTasks():
data-id="${task.id}" a cada botón..done).Ahora mismo tu lista tiene un problema molesto: si refrescas la página (o cierras la pestaña), todo desaparece.
Esto pasa porque tus tareas solo existen en la memoria de JavaScript. Cuando la página se recarga, esa memoria se reinicia.
localStorage viene integrado en el navegador. Piénsalo como una caja pequeña donde puedes guardar texto bajo un nombre (una “clave”). Es perfecto para proyectos de principiantes porque no necesita servidor ni cuentas.
Guardaremos toda la lista de tareas como texto JSON y la cargaremos cuando se abra la página.
Cada vez que añadas, marques o borres una tarea, llama a saveTasks().
const STORAGE_KEY = "todo.tasks";
function saveTasks(tasks) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tasks));
}
Donde sea que actualices el array tasks, haz esto justo después:
saveTasks(tasks);
renderTasks(tasks);
Cuando la página carga, lee el valor guardado. Si no hay nada guardado aún, usa una lista vacía.
function loadTasks() {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? JSON.parse(saved) : [];
}
let tasks = loadTasks();
renderTasks(tasks);
Eso es todo: ahora la app recuerda las tareas aunque refresques.
Consejo: localStorage solo guarda cadenas, así que JSON.stringify() convierte tu array en texto y JSON.parse() lo vuelve a transformar en un array cuando lo cargas.
Probar suena aburrido, pero es la forma más rápida de convertir tu app de “funciona en mi máquina” a “funciona siempre”. Haz una pasada rápida tras cada cambio pequeño.
Recorre el flujo principal en este orden:
Si algún paso falla, arréglalo antes de añadir nuevas funciones. Las apps pequeñas se vuelven caóticas si apilas problemas.
Los casos límite son entradas para las que no diseñaste, pero los usuarios reales las harán:
Un arreglo común es bloquear tareas vacías:
const text = input.value.trim();
addButton.disabled = text === "";
(Puedes ejecutar eso en cada evento input, y otra vez justo antes de añadir.)
Cuando los clics no hacen nada, suele ser una de estas:
id o nombre de clase difiere entre HTML y JS).app.js no se encontró).Cuando algo parece aleatorio, regístralo:
console.log("Adding:", text);
console.log("Current tasks:", tasks);
Revisa la Consola del navegador para errores (texto rojo). Cuando soluciones, elimina los logs para que no ensucien el proyecto.
Una app de tareas está “lista” cuando es cómoda para personas reales—en teléfonos, con teclado y con herramientas de ayuda como lectores de pantalla.
En pantallas pequeñas, botones diminutos frustran. Da espacio a los elementos clicables:
En CSS, aumentar padding, font-size y gap suele marcar la mayor diferencia.
Los lectores de pantalla necesitan nombres claros para los controles.
label real (la mejor opción). Si no quieres mostrarlo, puedes ocultarlo visualmente con CSS, pero mantenlo en el HTML.aria-label="Borrar tarea" para que el lector de pantalla no anuncie un botón sin contexto.Esto ayuda a que las personas entiendan qué hace cada control sin adivinar.
Asegúrate de que se pueda usar toda la app sin ratón:
form ayuda a que Enter funcione naturalmente).Usa un tamaño de fuente legible (16px es una buena base) y contraste fuerte (texto oscuro sobre fondo claro o viceversa). Evita usar solo color para indicar “hecho”: añade un estilo claro como tachado además del color.
Ahora que todo funciona, dedica 10–15 minutos a ordenar. Esto hace que arreglos futuros sean más fáciles y te ayuda a entender tu propio proyecto cuando vuelvas más tarde.
Manténlo pequeño y predecible:
/index.html — la estructura de la página (input, botón, lista)/styles.css — el aspecto (espaciado, fuentes, estilo “hecho”)/app.js — el comportamiento (añadir, alternar hecho, borrar, guardar/cargar)/README.md — notas rápidas para el “tú del futuro”Si prefieres subcarpetas, también puedes:
/css/styles.css/js/app.jsSolo asegúrate de que tus rutas en link y script coincidan.
Algunos trucos rápidos:
taskInput, taskList, saveTasks()Por ejemplo, es más fácil leer:
renderTasks(tasks)addTask(text)toggleTask(id)deleteTask(id)Tu README.md puede ser simple:
index.html en un navegador)Como mínimo, comprime la carpeta cuando termines un hito (por ejemplo, “localStorage funciona”). Si quieres historial, Git es genial—pero opcional. Incluso una copia de seguridad simple puede salvarte de borrados accidentales.
Publicar solo significa poner los archivos (HTML, CSS, JavaScript) en algún sitio público para que otras personas puedan abrir un enlace y usarla. Como esta app es un “sitio estático” (se ejecuta en el navegador y no necesita servidor), puedes alojarla gratis en varios servicios.
Pasos generales:
Si tu app usa archivos separados, revisa que los nombres coincidan exactamente con tus enlaces (por ejemplo styles.css vs style.css).
Si quieres la manera más sencilla “subir y listo”:
localStorage funcionando).Cuando pase esto, envía el enlace a un amigo y pídele que la pruebe—ojos frescos detectan problemas rápido.
Has construido una app de tareas que funciona. Si quieres seguir aprendiendo sin saltar a un proyecto grande, estas mejoras añaden valor real y enseñan patrones útiles.
Añade un botón “Editar” al lado de cada tarea. Al hacer clic, cambia la etiqueta por un pequeño input (prefill) y muestra “Guardar” y “Cancelar”.
Consejo: mantén los datos como un array de objetos (con id y text). Editar entonces es: encontrar la tarea por id, actualizar text, volver a renderizar y guardar.
Añade tres botones arriba: Todas, Activas, Hechas.
Guarda el filtro actual en una variable como currentFilter = 'all'. Al renderizar, muestra:
Mantenlo ligero:
YYYY-MM-DD) y muéstrala junto a la tareaIncluso un campo extra te enseña a actualizar el modelo de datos y la UI juntos.
Cuando estés listo, la idea grande es: en vez de guardar en localStorage, envías las tareas a una API (un servidor) usando fetch(). El servidor las guarda en una base de datos, así las tareas se sincronizan entre dispositivos.
Si quieres probar ese salto sin rehacer todo, plataformas de prototipado y generación de código pueden ayudarte a crear la versión siguiente con endpoints API y persistencia.
Prueba a construir una app de notas (con búsqueda) o un rastreador de hábitos (check-ins diarios). Reutilizan las mismas habilidades: renderizado de listas, edición, guardado y diseño de UI simple.