Trabajo / Front-end / SKÅL Madrid · La web en Angular
El formulario decía «hemos recibido tu solicitud» y no la recibía nadie
Proyecto 11 de 28 del archivo 02 de 08 en Desarrollo front-end Ver la disciplina completa ↗
Ábrelo en grande: la carta, la galería y el formulario de reservas
El marco de aquí debajo cambia entre escritorio y teléfono, que es donde se ve lo que más trabajo costó. Para recorrerlo entero hace falta pantalla, así que también se abre en una pestaña nueva.
Una auditoría con tres bloqueantes
La versión anterior no estaba mal hecha: era una one-page honesta, sin librerías, con la carta explicada plato a plato —su mayor fortaleza según el benchmark— y sin una sola petición a terceros. El problema no era el maquetado. Era que lo que más importa en la web de un restaurante no funcionaba.
El informe UX/CRO marcaba tres cosas como bloqueantes. La primera: el formulario de reservas mostraba «hemos recibido tu solicitud» y los datos no llegaban a ningún destino. Cada reserva hecha por la web se perdía, y el cliente se presentaba creyendo que tenía mesa. La segunda: teléfono, dirección y perfiles eran texto plano, sin un solo enlace donde pulsar. La tercera: en un negocio cuyo argumento principal es el sitio, no había ni una fotografía del local publicada.
Lo que se me pide es implementar el informe entero, no interpretarlo. Trece acciones, con su orden y su justificación, y una identidad visual que no se toca.
Acciones implementadas
La decimotercera, un motor de reservas de pago, queda fuera de alcanceBloqueantes resueltos
Formulario, datos de contacto y fotografías del localCombinaciones AA
Toda la paleta medida, ninguna por debajo de WCAG 2.1 AAPruebas unitarias
Sobre el horario y los validadores de reservaLa identidad se queda como estaba
La paleta, las tipografías y el tono koselig vienen de la versión anterior y no se rediseñan: se migran a tokens para poder tocarlos en un sitio y que cambien en todos. Lo único que cambia de color son los cuatro incumplimientos de contraste que señalaba el informe, y cambian lo justo para cumplir: el madera sobre crema pasa de 3,11:1 a 5,11:1, y el texto legal del pie de 3,97:1 a 7,29:1.
Angular, pero que se lea sin JavaScript
La web es Angular 21 con componentes independientes, señales y detección de cambios sin Zone.js: ni un NgModule ni zone.js en el paquete. Y está prerenderizada (outputMode: 'static'), así que cada una de las cinco rutas se compila a un HTML completo.
Eso último no es un detalle de despliegue, es la decisión que ordena el resto: un buscador, una tarjeta de WhatsApp y el primer pintado reciben contenido real, no un <app-root> vacío. Las animaciones de entrada y el fundido de las fotos se activan solo cuando hay JavaScript, nunca al revés.
01 · Un formulario que no confirma lo que no ha pasado
El servicio de reservas envía la solicitud por POST con tiempo máximo de espera y distingue el fallo de red del fallo de servidor. El diálogo de confirmación está atado al resultado real del envío: si el destino no responde bien, no se abre.
Y mientras el endpoint no esté conectado, la web lo dice antes de que nadie escriba nada y ofrece teléfono y WhatsApp, en vez de simular que ha ido bien. Es la banda ámbar de la captura, y es exactamente el bloqueante nº 1 resuelto al revés de como estaba.
nucleo/servicios · reservas.tsConfirmación atada al envío · comprobado en pruebas
02 · El horario, una sola fuente de verdad
El selector de hora solo ofrece pases que existen: 13:00 a 16:00 y 19:30 a 23:00, en tramos de media hora; los lunes se rechazan con su motivo y los pases de hoy que ya han pasado desaparecen. Se acabaron las llamadas de rechazo.
Ese horario vive en un único sitio y desde ahí alimenta a la vez el indicador «abierto ahora», los pases del formulario, el cuadro de horarios y los datos estructurados de Google. No pueden desincronizarse, que es como se desincronizan siempre.
nucleo/servicios · horario.ts · validadores.ts15 pruebas unitarias
aria-controls03 · Las fotos del local, que era el hueco más caro
Cuatro fotografías reales donde antes había recuadros vacíos con texto de trabajo interno: el comedor bajo el drakkar, el rincón de los sofás, la barra con sus pizarras y la fachada. Se amplían con <dialog> nativo, con el foco atrapado, cierre con Esc y flechas para pasar de foto.
Y pesan lo que deben. Un guion con Sharp genera cuatro anchos en WebP, el srcset, el sizes y una miniatura difuminada para el fundido de entrada. La foto que la auditoría señalaba, de 1,9 MB, se sirve hoy en 83 kB.
scripts · optimizar-imagenes.mjs1.855 kB → 83 kB en la peor de ellas
<dialog>, foco atrapado y teclado04 · En el teléfono, reservar es un toque
El informe medía la conversión en toques, y reservar costaba dos porque el botón vivía dentro del menú hamburguesa. Ahora «Reservar» está siempre visible en la cabecera, y abajo hay una barra fija con Llamar · WhatsApp · Reservar mesa que aparece pasado el hero.
Con un detalle que se ve en las dos capturas: la barra se aparta sola cuando el formulario ya está en pantalla. Insistir en ese momento sería taparle al usuario justo lo que ha venido a hacer.
disposicion · barra-accionDe dos toques a uno · áreas táctiles de 44 px
Nada se carga sin permiso, y la medición tampoco
De serie no hay una sola petición a terceros: las tipografías son del sistema y los iconos van incrustados en SVG. Google Analytics y Clarity solo se cargan si se acepta el aviso de cookies, y los doce eventos de conversión que ocurren antes de esa decisión se encolan y se envían al aceptar, o se descartan si no. Era una fortaleza de la versión anterior y no había motivo para perderla.
La web sabe lo que todavía no tiene
Esto no está publicado todavía. Faltan cosas que no dependen del código: el teléfono definitivo, el CIF para el aviso legal, el destino real del formulario, los identificadores de medición y las reseñas de Google. Todo eso está declarado en un único fichero, y mientras siga ahí la web lo dice en pantalla en vez de disimularlo.
Por eso el formulario avisa de que el envío no está conectado, y por eso el bloque de opiniones dice que son de ejemplo. Hay una consecuencia que no se ve y me parece la más importante: mientras las reseñas sean de ejemplo, la nota media no se declara en los datos estructurados. Publicar un 4,7 inventado en Schema.org sería mentirle a Google y al cliente que viene por esa estrella.
Menos peso en la peor foto
Los 1.855 kB del gravlaks, servidos en 83 kBDe JavaScript comprimido
Todo el sitio, sin una librería de tercerosPeticiones a terceros de serie
Tipografías del sistema e iconos SVG incrustadosRutas prerenderizadas
Portada, aviso legal, privacidad, cookies y créditosUn recordatorio que solo ve quien programa
En desarrollo, la propia web muestra abajo a la izquierda la lista de lo que queda pendiente; en producción no aparece. Es la manera de que la lista no viva en un documento que nadie vuelve a abrir, sino delante de quien puede tacharla. Y las fotografías de los platos, que vienen de Wikimedia Commons con licencia libre, tienen su atribución dentro de la propia web, en /creditos.
El sitio, funcionando
Aquí debajo está la web entera: recorre la carta cambiando de pestaña, abre una fotografía del local, mira cómo el indicador de arriba sabe si el restaurante está abierto ahora mismo y prueba a pedir una hora que no existe.
Y usa el control de arriba a la derecha: pon el marco a tamaño teléfono y verás aparecer la barra fija de abajo y salir «Reservar» del menú. No hay escalado; se le está entregando un ancho distinto.
Esta copia es la compilación real, con los datos provisionales que el restaurante aún no ha cerrado: el teléfono, el CIF y las reseñas son de ejemplo, y la web lo dice donde toca. No se ha maquillado nada para la demostración; se enseña el estado en el que está.
Angular 21 · sin Zone.js · prerenderizado · 148 KB de JavaScript (44 KB comprimido)
De dónde sale
Esta ficha es la mitad de un trabajo. La otra mitad es la auditoría digital y el informe UX/CRO que la ordenaron: el análisis de la versión anterior, el benchmark, los cuatro incumplimientos de accesibilidad medidos uno a uno y el plan de trece acciones priorizadas.
Escribir el informe y luego implementarlo cambia cómo se escribe el informe. Cuando sabes que vas a tener que resolver tú el «conectar el formulario a un destino real con gestión de errores», dejas de redactar recomendaciones y empiezas a redactar cosas que se pueden terminar. También te obliga a lo contrario: la acción trece —un motor de reservas de pago— sigue marcada como fuera de alcance, porque es una decisión de negocio y no un problema de código.
Siguiente en Desarrollo front-end
Design System EdG