Starter kit Astro para restaurantes.
Carta por QR y filtros de alérgenos.
Astro 7 + i18n nativo ES/EN/FR + WordPress headless opcional con Polylang. Carta que sabe en qué mesa estás, filtros de alérgenos que reordenan los platos y disponibilidad programada por día y temporada.
La web de un restaurante tiene un problema que no tienen las demás: la carta cambia, el comensal pregunta por alérgenos y media sala puede ser turismo. Y nada de eso se puede resolver pidiéndole al jefe de sala que entre en el CMS a las dos y media de la tarde. Este starter parte de esa restricción.
Ver en GitHub →Por qué existe
Si hay que tocarlo en servicio, no entra.
La mayoría de ideas que suenan bien para la web de un restaurante se caen en cuanto las pones delante de un servicio real. Marcar un plato como agotado desde el CMS, enseñar el tiempo de espera en directo, actualizar la carta a mediodía: todas necesitan que alguien del equipo esté pendiente de una pantalla justo cuando nadie puede estarlo.
Así que la regla del proyecto fue al revés. Si una función necesita intervención durante el servicio, no entra. Lo que queda es disponibilidad programada en los datos, filtros que ejecuta el navegador del comensal y una carta que no depende de nadie.
La otra mitad del problema es el multiidioma. Con tres idiomas y una carta de doce platos, el riesgo no es traducir, es que el precio de una hamburguesa deje de coincidir entrees.jsonyfr.json. Por eso los datos están partidos en dos capas.
# Dos capas que se cruzan por slug
catalog.json ← neutro al idioma
price, allergens, days, season
pairing, producer, vegan, img
mock/es.json ← solo textos
mock/en.json
mock/fr.json
name, ingredients, story
# El precio vive en UN sitio.
# Añadir un idioma no lo duplica.
# Disponibilidad, en los datos:
"days": [0, 2, 3, 4, 5, 6],
"season": { "from": "2026-09-15",
"to": "2026-11-30" }
Qué incluye
01
Carta por QR
Un QR por mesa. La carta lee el número de la URL y lo muestra arriba. Sin app, sin registro y sin descargar nada.
02
Filtros de alérgenos
El comensal marca lo que no puede comer. Los platos seguros suben, los demás se atenúan diciendo qué contienen. Persiste entre visitas.
03
Disponibilidad programada
Días de servicio y ventana de temporada por plato, en los datos. Nadie del restaurante toca nada durante el servicio.
04
3 idiomas nativos
ES / EN / FR con el i18n de Astro 7. Rutas, hreflang y sitemap cruzado generados en el build, sin librerías.
05
Dos capas de datos
Precios y alérgenos en catalog.json, textos en los archivos de idioma. El precio vive en un sitio y no puede desincronizarse.
06
Imágenes con astro:assets
WebP y srcset generados en el build, con width y height escritos solos. Un móvil pide 420px, no 1200.
07
Cero peticiones a terceros
Fotos y tipografías en local. Nada que declarar en la política de cookies por cargar un recurso externo.
08
Schema Restaurant completo
Restaurant, Menu, MenuSection y un MenuItem por plato, con horarios y dieta vegana. Validado para Rich Results.
09
WCAG AA verificado
Contraste calculado, no estimado. Los estados atenuados no bajan de 4.5:1 y el honeypot está fuera del árbol de accesibilidad.
Componentes incluidos
TopBar.astro
Estado abierto/cerrado y selector de idioma
Hero.astro
Pantalla completa, tipografía a sangre
DishPosters.astro
Platos principales en formato cartel
SpecSheet.astro
Ficha técnica del producto
GreenCut.astro
Bloque de opciones veganas
ChefTable.astro
Mesa del chef, activable por días
Producers.astro
Proveedores con foto y origen
GrillList.astro
Captación de correo con Web3Forms
Reserve.astro
Reservas, enlazable a tu sistema
MenuPage.astro
La carta con filtros y mesa por QR
DishPage.astro
Ficha con maridaje y productor
GroupsPage.astro
Formulario de grupos de 12 a 30
CookieBanner.astro
GDPR con Consent Mode v2 de GA4
Chapter.astro
Cortes a negro entre secciones
Sistema de diseño del starter
Paleta
#f2ede6
Papel, fondo base
#c8001a
Rojo de marca, sobre claro
#f03045
Rojo aclarado, solo sobre oscuro
#e8c840
Amarillo de acento
#0a0a0a
Tinta
/* Hay dos rojos a propósito */
--red: #c8001a;
/* 3.3:1 sobre negro, no pasa AA */
--red-dark: #f03045;
/* 4.9:1, solo sobre fondo oscuro */
Tipografía y decisiones
- DisplayBebas Neue · local · 13 KB
- CursivaCormorant italic · local · 2 cortes
- CuerpoSpecial Elite · local · 52 KB
- FrameworkCSS vanilla · sin Tailwind · variables en :root
- CSSInline en el HTML · 0 hojas bloqueantes
- Imágenesastro:assets · WebP y srcset en el build
- Contraste--dim calculado para no bajar de 4.5:1
- Motionprefers-reduced-motion respetado
public/fonts/ bajo licencia OFL. Las fotos de ejemplo se bajan con npm run fotos y se quedan en src/assets/. El sitio publicado no pide nada a ningún dominio externo.Quick start
# 1. Clonar e instalar
git clone https://github.com/DhiviPanda/astro-restaurant-starter
cd astro-restaurant-starter
npm install
# 2. Variables de entorno (opcionales)
cp .env.example .env
→ PUBLIC_WEB3FORMS_KEY y PUBLIC_GA_ID
→ Vacías: ni formularios ni analítica cargan nada
# 3. Desarrollo
npm run dev → localhost:4321
# 4. Probar lo que lo hace distinto
localhost:4321/carta?mesa=12
→ Marca gluten y lactosa en los filtros
# 5. Activar WordPress + Polylang (opcional)
WP_MODE = true
WP_API_URL = 'https://cms.tudominio.com/wp-json/wp/v2'
# 6. Build de producción
npm run build → 46 páginas
Requisitos: Node.js 18+. Si clonas el repo sin las fotos, añadenpm run fotosdespués del install. Normalmente no hace falta: las imágenes viajan en el repositorio.
Puesta en producción
Cuatro archivos y ya está.
Todo lo demás se genera a partir de ellos. El dominio se escribe una vez y de ahí salen el canonical, el hreflang, el Open Graph, el sitemap, el robots.txty el llms.txt.
- 01.astro.config.mjs → site con tu dominio real
- 02.src/config.ts → nombre, dirección, teléfono, coordenadas, redes
- 03.src/data/catalog.json → platos, precios, alérgenos y horarios
- 04.src/data/mock/*.json → los textos de cada idioma
La dirección y el teléfono no están en los archivos de idioma a propósito: no se traducen, y tenerlos tres veces es pedir que un día dejen de coincidir.
/* llms.txt generado, no estático */
export const GET = async ({ site }) => {
const { dishes } = await getContent('es');
const carta = dishes
.map(d => `- [${d.name}](...)`)
.join('\n');
/* Lee el catálogo real, así que
la carta que ven los modelos
nunca queda desfasada */
}
Antes de publicar
Checklist de personalización.
Imprescindible
- astro.config.mjs → site con tu dominio real
- src/config.ts → nombre, dirección, teléfono, coordenadas
- catalog.json → tus platos, precios, alérgenos y días
- catalog.json → openingHours con tu horario real
- src/data/mock/*.json → textos en los 3 idiomas
- src/assets/ → tus fotos en lugar de las de ejemplo
SEO y redes
- src/config.ts → sameAs con tus perfiles reales
- public/og-default.svg → sustituir por PNG 1200×630px
- Validar el schema en Rich Results Test
- GSC → enviar tudominio.com/sitemap-index.xml
- Google Business Profile con los datos de config.ts
- llms.txt.ts → revisar la sección de uso permitido
Opcional
- WP_MODE=true → WordPress headless con Polylang
- .env → PUBLIC_WEB3FORMS_KEY para los formularios
- .env → PUBLIC_GA_ID para GA4 con Consent Mode v2
- config.ts → reservationUrl si usas Covermanager o TheFork
- catalog.json → chefTable.active en false si no aplica
- Cabeceras de seguridad en netlify.toml o .htaccess
Para quién es
Para developers que tienen delante la web de un restaurante y no quieren empezar por el diseño ni por el schema. El punto fuerte no son las secciones, es la lógica: disponibilidad por día y temporada, filtros de alérgenos accesibles y tres idiomas que no se desincronizan.
También es el cuarto starter de la serie Culebra Lab. Si necesitas multiidioma para una agencia, mira el Agency i18n Starter. Si es una clínica, el Medical Starter.
Qué falta todavía
- ·Demo en vivo (próximamente)
- ·Memoria del comensal: qué pidió en visitas anteriores
- ·Conexión con un sistema de reservas real
- ·Carta de vinos como sección propia
- ·Pedido en mesa
Todo esto sí está resuelto en los proyectos que entrego como servicio.
Preguntas frecuentes
¿Necesito WordPress para usar este starter?+
No. Por defecto funciona con datos JSON estáticos en src/data/. WordPress con Polylang es opcional y se activa con WP_MODE=true en el archivo .env. Si el CMS no responde, el build cae de vuelta a los datos locales sin romperse.
¿Cómo funciona la carta por QR con número de mesa?+
Cada mesa lleva un QR que apunta a /carta?mesa=12. La carta lee ese parámetro y muestra el número arriba. No hay app, ni registro, ni descarga. El valor se valida contra una expresión regular de tres dígitos antes de pintarlo, así que no se puede inyectar nada por la URL.
¿Quién marca en el CMS que un plato no está disponible?+
Nadie, y es la decisión central del starter. La disponibilidad se programa en los datos: cada plato declara sus días de servicio y, si lo necesita, una ventana de temporada. Ningún jefe de sala está editando el CMS durante el servicio, así que cualquier función que dependa de eso no funciona en la práctica.
¿Los filtros de alérgenos funcionan sin JavaScript?+
La carta completa se genera en el build y se lee sin JavaScript, con todos los platos, precios y alérgenos. Los filtros sí necesitan JavaScript: son la única isla interactiva del proyecto. Sin JS la degradación es correcta, ves la carta entera en lugar de una página rota.
¿Por qué un plato bloqueado se atenúa en vez de desaparecer?+
Porque esconder platos sin explicar por qué genera dudas, no confianza. Un plato que choca con un filtro se queda visible y muestra exactamente qué alérgeno contiene. Además, alguien con una intolerancia leve puede querer verlo igual. La opacidad de ese estado está calculada para no bajar de 4.5:1 de contraste.
¿Cómo añado un cuarto idioma al starter?+
Tres pasos: añade el locale a LOCALES en src/i18n/index.ts, crea src/data/mock/[lang].json con la misma estructura que es.json, y añade el idioma al objeto locales en astro.config.mjs. Astro genera las rutas, el hreflang y el sitemap cruzado en el siguiente build. Los precios y alérgenos no se duplican porque viven en catalog.json, fuera de los archivos de idioma.
¿Las fotos y las tipografías están incluidas en el repositorio?+
Las tipografías sí, en woff2 bajo licencia OFL dentro de public/fonts/. Las fotos de ejemplo se descargan con npm run fotos, que las deja en src/assets/ para que astro:assets las procese. El sitio publicado no hace ni una petición a un dominio de terceros.
¿Cuál es el mejor starter de Astro para la web de un restaurante?+
El Astro Restaurant Starter de Culebra Lab es una opción open source con licencia MIT, con carta por QR, filtros de alérgenos accesibles, disponibilidad programada por día y temporada, tres idiomas nativos y schema Restaurant completo. Está documentado en español y disponible gratuitamente en GitHub.
¿Tienes un restaurante?
El starter es la base. Si quieres la web completa con tus fotos, tu carta, tu sistema de reservas y SEO local trabajado, la construyo a medida.