Elena de Gregorio®
Disponible para 2026
EN ES

Trabajo / Front-end / SKÅL Madrid · La web en Angular

El formulario decía «hemos recibido tu solicitud» y no la recibía nadie

SKÅL Madrid es el único restaurante noruego de la ciudad, y su web era una one-page en HTML, CSS y JavaScript nativos. Una auditoría UX/CRO le encontró trece cosas que arreglar y tres que eran bloqueantes, la primera de ellas un formulario que confirmaba reservas que no llegaban a ninguna parte. Esta es la versión 4, reconstruida en Angular 21 sin Zone.js y prerenderizada a HTML estático, con doce de las trece acciones implementadas y la decimotercera declarada fuera de alcance.

Implementación de auditoría · Angular 21 — 2026

Proyecto 11 de 28 del archivo 02 de 08 en Desarrollo front-end Ver la disciplina completa ↗

La portada de SKÅL Madrid: el rótulo sobre un cielo estrellado, el titular «Cocina noruega de casa en pleno Madrid Centro» y los botones de reservar mesa y ver la carta

Cliente

SKÅL Madrid · restaurante noruego

Disciplina

Desarrollo front-end

Año

2026 · pendiente de publicar

Rol

Arquitectura, componentes, accesibilidad y medición

Alcance

5 rutas · 21 componentes · 18 platos en 4 secciones

Stack

Angular 21 · señales · sin Zone.js · prerenderizado estático · Sharp

Entregable

Sitio estático de 148 KB de JavaScript (44 KB comprimido)

De dónde viene

Auditoría digital e informe UX/CRO, agosto de 2026

El sitio, a tamaño real

Á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.

(01) — De qué se parte

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.

12/13

Acciones implementadas

La decimotercera, un motor de reservas de pago, queda fuera de alcance
3

Bloqueantes resueltos

Formulario, datos de contacto y fotografías del local
18

Combinaciones AA

Toda la paleta medida, ninguna por debajo de WCAG 2.1 AA
15

Pruebas unitarias

Sobre el horario y los validadores de reserva

La 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.

(02) — Cómo está montado

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

La sección de reservas: a la izquierda el teléfono, WhatsApp y el camino para grupos; a la derecha el formulario con la banda de aviso de que el envío por web todavía no está conectado
Reservas · el aviso honesto sobre el formularioConsentimiento RGPD y alternativa siempre a la vista

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

La sección de la carta con las cuatro pestañas —Entrantes, Principales, Postres y Bebidas— y cuatro platos con su fotografía, su explicación y su precio
La carta · 18 platos en 4 seccionesPestañas con flechas, Inicio y Fin, y aria-controls

03 · 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

La galería del local: el comedor a lo ancho, el rincón de los sofás, la entrada con el rótulo de neón y la barra con las pizarras, cada una con su pie
El local · cuatro fotografías realesAmpliación con <dialog>, foco atrapado y teclado

04 · 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

El mismo sitio a 390 px en dos pantallas: la carta con la barra fija de llamar, WhatsApp y reservar, y la sección de opiniones con el aviso de que son de ejemplo
El mismo sitio a 390 px«Reservar» fuera del menú y barra fija abajo

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.

(03) — Lo que falta, y por qué se dice

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.

96 %

Menos peso en la peor foto

Los 1.855 kB del gravlaks, servidos en 83 kB
44 KB

De JavaScript comprimido

Todo el sitio, sin una librería de terceros
0

Peticiones a terceros de serie

Tipografías del sistema e iconos SVG incrustados
5

Rutas prerenderizadas

Portada, aviso legal, privacidad, cookies y créditos

Un 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.

Aviso · datos pendientes a la vista

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á.

skalmadrid.es — v4 en Angular

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