HTML, CSS y JavaScript
Fundamentos de la web: HTML semántico y formularios, CSS con Flexbox, Grid y diseño adaptable, y JavaScript moderno (ES6+): funciones, DOM, asincronía con promesas y async/await, y la Fetch API.
Definición
Toda página web se arma con tres lenguajes que el navegador entiende sin instalar nada. Cada uno tiene un trabajo distinto:
| Lenguaje | Qué es | Su trabajo | En el restaurante |
|---|---|---|---|
| HTML | Lenguaje de marcado (HyperText Markup Language) | La estructura: qué hay en la página y qué es cada cosa | La carta: qué platillos hay, sus nombres y precios |
| CSS | Hojas de estilo en cascada (Cascading Style Sheets) | La presentación: colores, letras, tamaños y acomodo | El diseño de la carta: tipografía, colores y orden |
| JavaScript | Lenguaje de programación | El comportamiento: reaccionar a clics, pedir datos y cambiar la página | Lo que pasa cuando tocas «Agregar»: el pedido se arma y el total se suma |
En este módulo construimos la página del menú del Restaurante en línea con los tres. Es el mismo menú que en el módulo 3 pasará a React; lo que aprendas aquí es exactamente lo que React usa por dentro.
HTML: la estructura
HTML describe el contenido con etiquetas. Una etiqueta abre y cierra un elemento (<h1>…</h1>) y puede llevar atributos (<img src="pozole.jpg" alt="Plato de pozole rojo">).
Lo importante es usar la etiqueta que dice qué es cada cosa (HTML semántico), no la que «se ve» como uno quiere:
| Etiqueta | Para qué | En el menú |
|---|---|---|
<header> | Encabezado de la página o de una sección | Nombre del restaurante |
<main> | El contenido principal (uno por página) | Menú y pedido |
<section> | Una parte con su propio título | El menú |
<nav> | Un grupo de enlaces o controles de navegación | Filtros por categoría |
<aside> | Contenido complementario | El resumen del pedido |
<ul> / <li> | Lista y sus elementos | Los platillos |
<button> | Una acción | «Agregar», «Hacer pedido» |
<form>, <label>, <input> | Un formulario y sus campos | La propina |
<template> | Un molde que no se muestra; JavaScript lo copia | La tarjeta de un platillo |
Por qué importa la semántica:
- Accesibilidad. Un lector de pantalla anuncia «botón, Agregar» o «región principal». Un
<div>con un clic no dice nada. - Buscadores. Google entiende mejor la página cuando las etiquetas dicen qué es cada cosa.
- Mantenimiento. Seis meses después,
<aside class="pedido">se entiende solo.
Cada <input> necesita su <label for="…">: al tocar la etiqueta se activa el campo, y el lector de pantalla sabe qué se pide.
CSS: la presentación
CSS aplica reglas a los elementos que elige un selector:
.platillo .precio { /* selector: el precio dentro de cada platillo */
font-weight: 700; /* propiedad: valor */
}
| Selector | Elige | Ejemplo |
|---|---|---|
| Etiqueta | Todos los elementos de ese tipo | body, h1 |
| Clase | Los que tienen class="…" (el más usado) | .platillo |
| Id | Uno solo con id="…" | #total |
| Atributo | Los que tienen un atributo o valor | [aria-pressed="true"] |
| Descendiente | Elementos dentro de otros | .pedido form |
La cascada. Si dos reglas chocan, gana la más específica (un id pesa más que una clase, y una clase más que una etiqueta) y, a igual peso, la que aparece después. Por eso conviene estilizar casi todo con clases.
El modelo de caja. Todo elemento es una caja: contenido, padding (relleno), border (borde) y margin (separación). Con box-sizing: border-box el ancho que declaras incluye el relleno y el borde, y las cuentas salen como esperas.
Variables. --acento: #1f7a4a; en :root define un color que se reutiliza con var(--acento). Cambiarlo en un lugar cambia toda la página.
Flexbox y Grid
Son las dos herramientas modernas para acomodar elementos:
| Flexbox | Grid | |
|---|---|---|
| Dimensión | Una (fila o columna) | Dos (filas y columnas) |
| Úsalo para | Una fila de botones, alinear cosas dentro de una tarjeta | El acomodo de la página, una cuadrícula de tarjetas |
| En el menú | .filtros y el interior de cada .platillo | .contenido (menú y pedido) y .platillos |
La cuadrícula de platillos se adapta sola, sin media queries:
.platillos {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 16px;
}
«Tantas columnas como quepan, cada una de al menos 180 px». En el celular sale una; en la computadora, tres o cuatro.
Diseño adaptable (responsive)
Más de la mitad de los pedidos llegan desde un celular. Tres piezas lo resuelven:
- La etiqueta
<meta name="viewport" content="width=device-width, initial-scale=1">en el<head>. Sin ella, el celular muestra la versión de computadora en miniatura. - Unidades relativas y
clamp():font-size: clamp(1.6rem, 5vw, 2.4rem)crece con la pantalla, sin pasarse. - Media queries para los cambios grandes:
@media (max-width: 760px) {
.contenido { grid-template-columns: 1fr; } /* el pedido baja debajo del menú */
}
JavaScript: el comportamiento
JavaScript corre en el navegador y puede leer y cambiar la página mientras está abierta. Lo moderno (ES6 en adelante) que usamos:
| Herramienta | Para qué | Ejemplo |
|---|---|---|
const / let | Declarar variables (const si no cambia) | const pedido = new Map(); |
| Funciones flecha | Funciones cortas | (p) => p.categoria === "bebidas" |
| Plantillas de texto | Texto con valores | ` ${cantidad} × ${platillo.nombre} ` |
filter, find, map | Recorrer arreglos sin for manual | menu.filter(...) |
Map | Pares llave → valor (id → cantidad) | pedido.set(id, 3) |
?? | Valor por defecto si es null o undefined | pedido.get(id) ?? 0 |
Intl.NumberFormat | Formato de moneda local | $1,250.00 |
El DOM
El navegador convierte el HTML en un árbol de objetos: el DOM (Document Object Model). JavaScript lo recorre y lo cambia:
const total = document.querySelector("#total"); // buscar un elemento
total.textContent = "$45.00"; // cambiar su texto
const li = document.createElement("li"); // crear uno nuevo
li.textContent = "3 × Enchilada verde";
document.querySelector("#lineas").append(li); // ponerlo en la página
Usa textContent para poner texto. innerHTML interpreta etiquetas, y con datos que escribió alguien más abre la puerta a inyectar código en tu página (XSS).
Eventos
La página reacciona con escuchadores de eventos:
boton.addEventListener("click", () => agregar(platillo.id));
document.querySelector("#propina").addEventListener("input", dibujarPedido);
formulario.addEventListener("submit", (evento) => {
evento.preventDefault(); // no recargar la página
// ...armar y enviar el pedido
});
En los filtros usamos delegación: un solo escuchador en el contenedor .filtros que revisa qué botón se tocó con evento.target.closest("button"). Sirve aunque los botones cambien.
Asincronía: promesas, async/await y fetch
Pedir datos a otra computadora tarda. Si JavaScript esperara sin hacer nada, la página se congelaría. Por eso fetch devuelve una promesa: «te aviso cuando llegue». Con async/await el código se lee de arriba abajo:
async function cargarMenu() {
try {
const respuesta = await fetch("platillos.json");
if (!respuesta.ok) throw new Error(`HTTP ${respuesta.status}`);
menu = await respuesta.json(); // texto JSON → arreglo de objetos
dibujarMenu("todas");
} catch (error) {
estado.textContent = "No se pudo cargar el menú.";
}
}
Dos detalles que confunden al empezar:
fetchno falla con un 404 o un 500: la promesa se cumple igual. Por eso revisamosrespuesta.ok.fetchno funciona con archivos abiertos con doble clic (file://). Hay que servir la carpeta: para eso esservir.mjs.
En el módulo 4 cambiamos "platillos.json" por "/api/platillos", la ruta de Express, y el resto del código queda igual.
Práctica: el menú del restaurante
El código completo está en el repositorio, carpeta modulos/01-html-css-javascript:
| Archivo | Qué hace |
|---|---|
index.html | Estructura: encabezado, menú con filtros, pedido con propina y el molde de tarjeta |
estilos.css | Variables de color, Flexbox, Grid automático y la media query del celular |
platillos.json | Los 6 platillos con id, nombre, descripción, precio y categoría |
menu.js | Carga el menú con fetch, lo dibuja, filtra, arma el pedido y suma el total |
servir.mjs | Servidor estático solo con Node para abrir la página en localhost:5500 |
- Descarga el repositorio (o copia los cinco archivos en una carpeta).
- Abre una terminal en esa carpeta y ejecuta:
node modulos/01-html-css-javascript/servir.mjs
- Abre http://localhost:5500. Prueba:
- Toca «Agregar» tres veces en la enchilada verde: el pedido dice
3 × Enchilada verdey el total$45.00. - Escribe
10en la propina: el total pasa a$55.00. - Toca «Bebidas»: solo quedan el agua de jamaica y el café de olla.
- Achica la ventana: debajo de 760 px el pedido baja debajo del menú.
- Toca «Agregar» tres veces en la enchilada verde: el pedido dice
- Abre las herramientas del navegador (F12), pestaña Consola, y toca «Hacer pedido». Verás el JSON que en el módulo 4 viajará a la cocina.
Este módulo se probó con Node v24.14.1 y Chrome, en 375 px y en pantalla de computadora.
Errores comunes
| Síntoma | Causa | Arreglo |
|---|---|---|
| «No se pudo cargar el menú» al abrir con doble clic | fetch no lee archivos file:// | Abre con node servir.mjs |
Cannot read properties of null | El script corre antes de que exista el HTML | <script src="menu.js" defer> |
El total sale 4510 en lugar de 55 | El valor de un <input> siempre es texto | Number(input.value) (ver el quiz) |
| En el celular todo se ve diminuto | Falta la etiqueta viewport | Agrégala en el <head> |
| Un estilo no se aplica | Otra regla es más específica o va después | Revisa la pestaña Elementos → Estilos |
Quiz del módulo
¿Qué imprime este código?
const total = 45; // 3 enchiladas
const propina = "10"; // lo que escribió el cliente en un <input>
console.log(total + propina);
55"4510"(el texto 4510)NaN- Un error
Respuesta: 2. El valor de un <input> siempre llega como texto, aunque el campo sea type="number". Cuando sumas un número con un texto, JavaScript no suma: convierte el número en texto y los pega (concatena). Resultado: "4510". En un restaurante real, ese pedido de 45 pesos se cobraría como 4,510. El arreglo es convertir antes: total + Number(propina) da 55. Con -, o / JavaScript sí convierte a número ("10" 1 es 10); el + es el único que pega texto. Pruébalo tú: node -e 'console.log(45 + "10", 45 + Number("10"))'.
Referencias
La carta del restaurante. HTML es lo que dice la carta (qué platillos hay y sus precios), CSS es cómo se ve (letras, colores y orden) y JavaScript es lo que pasa cuando tocas «Agregar»: el pedido se arma y el total se suma.
Glosario: del inglés al español
| Semantic HTML | HTML semántico: etiquetas que dicen qué es cada cosa |
|---|---|
| Responsive design | diseño adaptable a cualquier pantalla |
| DOM | modelo de objetos del documento: la página como objetos que JS puede cambiar |
| Event listener | escuchador de eventos: función que corre cuando pasa algo, como un clic |
| Async / await | esperar una respuesta sin congelar la página |
| Fetch | función del navegador para pedir datos a un servidor |