# JAILGAMES — Plan Técnico Completo --- ## 1. Arquitectura General ### Decisión: Eleventy (11ty) como generador de sitio estático Se descartan tres alternativas y se elige una: | Opción | Ventajas | Inconvenientes | |---|---|---| | HTML/CSS/JS puro | Sin dependencias | Hay que duplicar HTML por cada juego; cualquier cambio en el layout obliga a tocar todos los archivos | | Astro / Vite | Moderno, componentes | Pesado para <10 juegos; requiere Node + build complejo | | Microframework (Flask, Express) | Dinámico | Necesita servidor con runtime activo; innecesario para contenido estático en ferias | | **Eleventy (11ty)** | **Plantillas Nunjucks, lee JSON/YAML nativamente, genera HTML puro, cero JS en el navegador** | **Requiere Node para el build (no en producción)** | **Justificación concreta:** - **Mantenimiento sencillo**: añadir un juego = crear un archivo YAML + copiar imágenes. Nada más. - **Cero dependencias en producción**: el resultado es HTML/CSS/JS estático servido por Nginx. - **Funciona sin internet**: perfecto para ferias presenciales. Se sirve desde un portátil con Docker. - **Nginx en lugar de Apache**: más ligero, configuración mínima para servir estáticos. Si se prefiere Apache, el cambio es trivial (se indica en la sección Docker). --- ## 2. Estructura de Carpetas ``` jailgames/ ├── src/ # ← Fuentes (nunca se sirven directamente) │ ├── _data/ │ │ └── site.json # Datos globales (nombre del sitio, redes, etc.) │ ├── _includes/ │ │ ├── base.njk # Layout base HTML (head, header, footer) │ │ ├── game-card.njk # Componente: tarjeta de juego para la home │ │ └── game-page.njk # Layout: ficha individual de juego │ ├── games/ # Un archivo YAML por juego │ │ ├── cosmic-escape.yaml │ │ ├── pixel-dungeon.yaml │ │ └── ... │ ├── css/ │ │ └── style.css # Estilos globales │ ├── js/ │ │ └── main.js # JS mínimo (lightbox, filtros, etc.) │ ├── index.njk # Página principal (home) │ └── game.njk # Plantilla que genera cada /game/slug/ │ ├── static/ # ← Archivos copiados tal cual al build │ ├── logo/ │ │ └── jailgames-logo.png # Logo general del sitio │ ├── games/ # Carpeta por juego (nombre = slug) │ │ ├── cosmic-escape/ │ │ │ ├── logo.png │ │ │ ├── screenshot-1.png │ │ │ ├── screenshot-2.png │ │ │ └── screenshot-3.png │ │ └── pixel-dungeon/ │ │ ├── logo.png │ │ └── ... │ └── favicon.ico │ ├── downloads/ # ← Binarios descargables │ ├── cosmic-escape/ │ │ ├── cosmic-escape-v1.2-windows.zip │ │ ├── cosmic-escape-v1.2-linux.tar.gz │ │ └── cosmic-escape-v1.2-mac.dmg │ └── pixel-dungeon/ │ └── ... │ ├── _site/ # ← Salida del build (se sirve con Nginx) │ ├── .eleventy.js # Configuración de Eleventy ├── package.json ├── Dockerfile ├── docker-compose.yml ├── nginx.conf # Configuración de Nginx └── README.md ``` ### Resumen de ubicaciones | Contenido | Ruta | |---|---| | Datos de cada juego | `src/games/.yaml` | | Imágenes de cada juego | `static/games//` | | Binarios descargables | `downloads//` | | Plantillas HTML | `src/_includes/` | | Página principal | `src/index.njk` | | Logo del sitio | `static/logo/jailgames-logo.png` | | CSS | `src/css/style.css` | | Salida final | `_site/` | --- ## 3. Formato de Datos por Juego Cada juego se define en un archivo YAML independiente dentro de `src/games/`. El nombre del archivo es el **slug** (identificador URL). ### Ejemplo: `src/games/cosmic-escape.yaml` ```yaml name: "Cosmic Escape" slug: "cosmic-escape" tagline: "Escapa de la estación antes de que colapse" description: | Un juego de plataformas 2D donde controlas a un astronauta atrapado en una estación espacial que se desmorona. Recoge oxígeno, esquiva meteoritos y encuentra la cápsula de escape antes de que se agote el tiempo. Desarrollado en Godot Engine. Incluye 12 niveles y modo contrarreloj. version: "1.2.0" release_date: "2025-11-15" tags: - plataformas - 2D - singleplayer logo: "/games/cosmic-escape/logo.png" screenshots: - "/games/cosmic-escape/screenshot-1.png" - "/games/cosmic-escape/screenshot-2.png" - "/games/cosmic-escape/screenshot-3.png" downloads: - platform: "Windows" file: "/downloads/cosmic-escape/cosmic-escape-v1.2-windows.zip" size: "85 MB" - platform: "Linux" file: "/downloads/cosmic-escape/cosmic-escape-v1.2-linux.tar.gz" size: "78 MB" - platform: "macOS" file: "/downloads/cosmic-escape/cosmic-escape-v1.2-mac.dmg" size: "90 MB" # Campos opcionales engine: "Godot 4.2" players: "1 jugador" genre: "Plataformas / Acción" repo: "https://gitea.local/jailgames/cosmic-escape" # Solo referencia interna ``` ### Campos obligatorios vs opcionales | Campo | Obligatorio | Descripción | |---|---|---| | `name` | Sí | Nombre visible del juego | | `slug` | Sí | Identificador para URLs y carpetas | | `description` | Sí | Texto descriptivo (soporta párrafos) | | `version` | Sí | Versión del juego | | `release_date` | Sí | Fecha de publicación (YYYY-MM-DD) | | `downloads` | Sí | Al menos una entrada con platform/file/size | | `screenshots` | Sí | Al menos una captura | | `logo` | No | Si no existe, se usa una imagen placeholder | | `tagline` | No | Frase corta para la tarjeta de la home | | `tags` | No | Etiquetas para filtrar | | `engine` | No | Motor de desarrollo | | `players` | No | Número de jugadores | | `genre` | No | Género del juego | | `repo` | No | Enlace al repositorio (solo referencia) | --- ## 4. Generación de Páginas ### 4.1 Configuración de Eleventy (`.eleventy.js`) ```javascript const yaml = require("js-yaml"); const fs = require("fs"); const path = require("path"); module.exports = function (eleventyConfig) { // Parsear archivos YAML eleventyConfig.addDataExtension("yaml,yml", (contents) => yaml.load(contents) ); // Copiar archivos estáticos al build eleventyConfig.addPassthroughCopy("static"); eleventyConfig.addPassthroughCopy("downloads"); eleventyConfig.addPassthroughCopy("src/css"); eleventyConfig.addPassthroughCopy("src/js"); // Colección: todos los juegos ordenados por fecha eleventyConfig.addCollection("games", function () { const gamesDir = path.join(__dirname, "src", "games"); const files = fs.readdirSync(gamesDir).filter((f) => f.endsWith(".yaml")); return files .map((f) => { const data = yaml.load( fs.readFileSync(path.join(gamesDir, f), "utf-8") ); return data; }) .sort((a, b) => new Date(b.release_date) - new Date(a.release_date)); }); // Filtro para formatear fechas eleventyConfig.addFilter("dateFormat", function (dateStr) { const d = new Date(dateStr); return d.toLocaleDateString("es-ES", { year: "numeric", month: "long", day: "numeric", }); }); // Filtro Markdown para descripciones const markdownIt = require("markdown-it"); const md = markdownIt({ html: true }); eleventyConfig.addFilter("markdown", (content) => md.render(content || "")); return { dir: { input: "src", includes: "_includes", data: "_data", output: "_site", }, templateFormats: ["njk", "md"], htmlTemplateEngine: "njk", }; }; ``` ### 4.2 Página Principal (`src/index.njk`) La home itera sobre la colección `games` y renderiza una tarjeta por cada juego. ```html --- layout: base.njk title: "JAILGAMES — Nuestros Juegos" ---

Juegos caseros, hechos con cariño

{% for game in collections.games %} {% include "game-card.njk" %} {% endfor %}
``` ### 4.3 Tarjeta de Juego (`src/_includes/game-card.njk`) ```html
{% if game.logo %} {{ game.name }} logo {% else %}
🎮
{% endif %}

{{ game.name }}

{% if game.tagline %}

{{ game.tagline }}

{% endif %}
v{{ game.version }} {% if game.tags %} {% for tag in game.tags %} {{ tag }} {% endfor %} {% endif %}
``` ### 4.4 Generación de Fichas Individuales (`src/game.njk`) Eleventy genera una página por cada archivo YAML mediante **paginación**: ```html --- pagination: data: collections.games size: 1 alias: game permalink: "/game/{{ game.slug }}/" layout: game-page.njk eleventyComputed: title: "{{ game.name }} — JAILGAMES" --- ``` ### 4.5 Layout de Ficha (`src/_includes/game-page.njk`) ```html {% extends "base.njk" %} {% block content %}
{% if game.logo %} {% endif %}

{{ game.name }}

{% if game.tagline %}

{{ game.tagline }}

{% endif %}
v{{ game.version }} {{ game.release_date | dateFormat }} {% if game.genre %}{{ game.genre }}{% endif %} {% if game.players %}{{ game.players }}{% endif %} {% if game.engine %}{{ game.engine }}{% endif %}
{% for img in game.screenshots %} Captura de {{ game.name }} {% endfor %}
{{ game.description | markdown | safe }}

Descargar

{% for dl in game.downloads %} {{ dl.platform }} {{ dl.size }} {% endfor %}
{% if game.tags %}
{% for tag in game.tags %} {{ tag }} {% endfor %}
{% endif %} ← Volver al catálogo
{% endblock %} ``` ### 4.6 Descargas Locales Las descargas se enlazan directamente a archivos en `/downloads//`. El atributo `download` en el `` fuerza la descarga en lugar de abrir el archivo en el navegador. Nginx sirve estos archivos como estáticos. No hay enlaces a Gitea ni servicios externos. --- ## 5. Diseño y Estilo ### 5.1 Framework CSS: PicoCSS Se recomienda **PicoCSS** (`@picocss/pico`) por: - Estilo limpio sin necesidad de clases (aplica estilos directamente a elementos HTML semánticos). - Muy ligero (~10 KB). - Soporte nativo de tema oscuro/claro. - Responsive por defecto. - Sin dependencias de build (se puede usar como archivo CSS estático). Si se prefiere más control visual, la alternativa es **Tailwind CSS** (requiere build step). ### 5.2 Esquema de Layout ``` ┌──────────────────────────────────────────────┐ │ HEADER │ │ [Logo JAILGAMES] Juegos caseros... │ ├──────────────────────────────────────────────┤ │ │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ │ Logo │ │ Logo │ │ Logo │ │ │ │ │ │ │ │ │ │ │ │ Name │ │ Name │ │ Name │ ← Grid │ │ │ Tag │ │ Tag │ │ Tag │ 3 cols │ │ └──────┘ └──────┘ └──────┘ │ │ │ │ ┌──────┐ ┌──────┐ │ │ │ ... │ │ ... │ │ │ └──────┘ └──────┘ │ │ │ ├──────────────────────────────────────────────┤ │ FOOTER │ │ JAILGAMES · Hecho con ♥ │ └──────────────────────────────────────────────┘ ``` **Ficha individual:** ``` ┌──────────────────────────────────────────────┐ │ ← Volver HEADER │ ├──────────────────────────────────────────────┤ │ │ │ [Logo] Nombre del Juego │ │ v1.2.0 · 15 nov 2025 · Godot │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ Galería de capturas │ │ │ │ [img1] [img2] [img3] │ │ │ └──────────────────────────────────────┘ │ │ │ │ Descripción del juego en prosa... │ │ Párrafo 1... │ │ Párrafo 2... │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ Windows │ │ Linux │ │ macOS │ │ │ │ 85 MB │ │ 78 MB │ │ 90 MB │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ #plataformas #2D #singleplayer │ │ │ ├──────────────────────────────────────────────┤ │ FOOTER │ └──────────────────────────────────────────────┘ ``` ### 5.3 Diseño Responsive - **Desktop (>1024px)**: Grid de 3 columnas para tarjetas. - **Tablet (768–1024px)**: Grid de 2 columnas. - **Móvil (<768px)**: 1 columna, capturas en scroll horizontal. ### 5.4 Paleta de Colores Sugerida ```css :root { --color-bg: #0f0f0f; --color-surface: #1a1a2e; --color-primary: #e94560; --color-secondary: #16213e; --color-text: #eaeaea; --color-text-muted: #a0a0a0; --color-accent: #0f3460; --color-success: #4ecca3; --font-main: "Inter", "Segoe UI", sans-serif; --font-heading: "Space Grotesk", "Inter", sans-serif; } ``` Tema oscuro por defecto (estética gaming), con contraste suficiente para legibilidad. --- ## 6. Pipeline de Actualización ### Añadir un juego nuevo ``` Paso 1 Crear el archivo YAML → src/games/.yaml (copiar de un juego existente y modificar campos) Paso 2 Crear carpeta de imágenes → static/games// Copiar: logo.png, screenshot-1.png, screenshot-2.png, ... Paso 3 Crear carpeta de descargas → downloads// Copiar binarios: -v-. Paso 4 Verificar que las rutas en el YAML coinciden con los archivos copiados. Paso 5 Regenerar el sitio: $ npm run build (Esto ejecuta: npx @11ty/eleventy) Paso 6 Verificar localmente: $ npm run serve (Esto ejecuta: npx @11ty/eleventy --serve) Abrir http://localhost:8080 y comprobar. Paso 7 Reconstruir el contenedor Docker: $ docker compose up --build -d ``` ### Modificar un juego existente ``` Paso 1 Editar src/games/.yaml Paso 2 Si hay imágenes nuevas, copiarlas a static/games// Paso 3 Si hay binarios nuevos, copiarlos a downloads// Paso 4 Regenerar: npm run build Paso 5 Reconstruir Docker: docker compose up --build -d ``` ### Diagrama del flujo ``` [YAML nuevo] + [Imágenes] + [Binarios] │ ▼ npm run build ← Eleventy lee YAML, genera HTML │ ▼ _site/ ← HTML/CSS/JS estático listo │ ▼ docker compose up ← Nginx sirve _site/ + downloads/ │ ▼ http://localhost ← Web lista para la feria ``` --- ## 7. Plan de Docker ### 7.1 Dockerfile ```dockerfile # ---------- ETAPA 1: Build ---------- FROM node:20-alpine AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci COPY src/ src/ COPY static/ static/ COPY .eleventy.js ./ RUN npx @11ty/eleventy # ---------- ETAPA 2: Servir ---------- FROM nginx:alpine # Configuración personalizada de Nginx COPY nginx.conf /etc/nginx/conf.d/default.conf # Copiar el sitio generado COPY --from=builder /app/_site /usr/share/nginx/html # Copiar descargas (no pasan por Eleventy) COPY downloads/ /usr/share/nginx/html/downloads/ EXPOSE 80 ``` ### 7.2 docker-compose.yml ```yaml version: "3.8" services: jailgames: build: . container_name: jailgames-web ports: - "80:80" volumes: # Volumen para descargas: permite actualizar binarios # sin reconstruir la imagen completa - ./downloads:/usr/share/nginx/html/downloads:ro # Volumen para imágenes de juegos (opcional, misma idea) - ./static/games:/usr/share/nginx/html/games:ro restart: unless-stopped ``` ### 7.3 nginx.conf ```nginx server { listen 80; server_name localhost; root /usr/share/nginx/html; index index.html; # Compresión para HTML/CSS/JS gzip on; gzip_types text/html text/css application/javascript application/json image/svg+xml; gzip_min_length 256; # Cacheo agresivo para assets estáticos (feria local) location ~* \.(png|jpg|jpeg|gif|svg|ico|webp)$ { expires 7d; add_header Cache-Control "public, immutable"; } # Descargas: forzar descarga en lugar de abrir en navegador location /downloads/ { add_header Content-Disposition "attachment"; sendfile on; tcp_nopush on; } # SPA fallback (por si se accede directamente a /game/slug/) location / { try_files $uri $uri/ $uri.html =404; } # Página 404 personalizada error_page 404 /404.html; } ``` ### 7.4 Uso con Apache (alternativa) Si se prefiere Apache, reemplazar la etapa 2 del Dockerfile: ```dockerfile FROM httpd:alpine COPY --from=builder /app/_site /usr/local/apache2/htdocs/ COPY downloads/ /usr/local/apache2/htdocs/downloads/ COPY apache.conf /usr/local/apache2/conf/httpd.conf EXPOSE 80 ``` ### 7.5 Despliegue en Ferias sin Internet ``` 1. En la máquina de desarrollo (con internet): $ docker compose build $ docker save jailgames-web:latest | gzip > jailgames-image.tar.gz 2. Copiar jailgames-image.tar.gz al portátil de la feria (USB). 3. En el portátil de la feria (sin internet): $ gunzip -c jailgames-image.tar.gz | docker load $ docker run -d -p 80:80 \ -v /ruta/local/downloads:/usr/share/nginx/html/downloads:ro \ --name jailgames jailgames-web:latest 4. Abrir http://localhost en cualquier navegador. (O configurar un punto de acceso WiFi para que otros dispositivos se conecten a la IP local del portátil) ``` --- ## 8. Lista de Tareas para Claude Code Copiar esta lista tal cual y dársela a Claude Code como instrucciones paso a paso. --- ### BLOQUE A — Inicialización del Proyecto ``` TAREA A1: Crear la estructura de carpetas del proyecto "jailgames" Crear los siguientes directorios: - src/_data/ - src/_includes/ - src/games/ - src/css/ - src/js/ - static/logo/ - static/games/ - downloads/ TAREA A2: Inicializar package.json Ejecutar: npm init -y Instalar dependencias: npm install --save-dev @11ty/eleventy js-yaml markdown-it Añadir scripts a package.json: "build": "npx @11ty/eleventy", "serve": "npx @11ty/eleventy --serve --port=8080", "clean": "rm -rf _site" ``` ### BLOQUE B — Configuración de Eleventy ``` TAREA B1: Crear .eleventy.js en la raíz del proyecto Contenido: Configuración de Eleventy que: - Parsea archivos YAML con js-yaml - Copia "static" y "downloads" como passthrough - Copia "src/css" y "src/js" como passthrough - Crea una colección "games" leyendo todos los .yaml de src/games/ - Ordena los juegos por release_date (más recientes primero) - Añade filtro "dateFormat" que formatea fechas en español - Añade filtro "markdown" que convierte texto a HTML con markdown-it - Input: src/, Output: _site/, Includes: _includes/, Data: _data/ TAREA B2: Crear src/_data/site.json Contenido: { "name": "JAILGAMES", "tagline": "Juegos caseros, hechos con cariño", "url": "http://localhost", "lang": "es" } ``` ### BLOQUE C — Plantillas HTML (Nunjucks) ``` TAREA C1: Crear src/_includes/base.njk Layout base HTML5 que incluya: - DOCTYPE html, lang="es" - Meta charset, viewport - Title dinámico: {{ title }} o fallback a site.name - Enlace a /css/style.css - Header con logo de JAILGAMES (enlazado a /) - Bloque {% block content %}{% endblock %} - Footer con "JAILGAMES · Hecho con ♥" - Enlace a /js/main.js antes de cerrar body TAREA C2: Crear src/_includes/game-card.njk Componente tarjeta de juego: - Enlace a /game/{{ game.slug }}/ - Imagen del logo del juego (o placeholder si no hay logo) - Nombre del juego

- Tagline si existe - Versión y tags en un div de metadatos TAREA C3: Crear src/_includes/game-page.njk Layout para ficha individual. Extiende base.njk. Incluye: - Header con logo del juego + nombre + tagline + metadatos (versión, fecha formateada, género, players, engine) - Sección de galería de capturas (iterar screenshots) - Sección de descripción (usar filtro markdown) - Sección de descargas: un botón por cada entrada en downloads (con atributo download, mostrar plataforma y tamaño) - Sección de tags - Enlace "← Volver al catálogo" a / TAREA C4: Crear src/index.njk Página principal: - Layout: base.njk - Sección hero con logo y subtítulo - Sección games-grid que itera collections.games e incluye game-card.njk para cada juego TAREA C5: Crear src/game.njk Plantilla de paginación que genera /game// para cada juego: - Front matter con pagination (data: collections.games, size: 1, alias: game) - Permalink: /game/{{ game.slug }}/ - Layout: game-page.njk - eleventyComputed title ``` ### BLOQUE D — Estilos CSS ``` TAREA D1: Crear src/css/style.css Archivo CSS completo que incluya: - Variables CSS (colores tema oscuro gaming, fuentes) - Reset mínimo (box-sizing, margin 0) - Body: fondo oscuro, color claro, fuente sans-serif - .site-header: flex, centrado, con logo a la izquierda - .site-logo: max-height 60px - .hero: centrado, padding generoso - .hero-subtitle: color muted - .games-grid: CSS Grid, 3 columnas en desktop, 2 en tablet, 1 en móvil - .game-card: bloque con fondo surface, borde redondeado, hover con sombra, transición suave. Sin text-decoration. - .game-card__image img: object-fit contain, altura fija - .game-card__title: color blanco - .game-card__tagline: color muted, font-size pequeño - .game-card__tag: badge pequeño con color accent - .game-detail: max-width 900px, centrado, padding - .game-detail__header: flex, logo a la izquierda, info a la derecha - .game-detail__screenshots: grid o flex con scroll horizontal en móvil - .game-detail__screenshot: borde redondeado, max-width 100% - .download-buttons: flex, wrap - .download-btn: botón grande con color primary, hover, icono de plataforma si es posible - .tag: badge igual que game-card__tag - .back-link: estilo enlace simple - .site-footer: centrado, padding, color muted - Media queries para responsive (768px, 1024px) ``` ### BLOQUE E — JavaScript Mínimo ``` TAREA E1: Crear src/js/main.js Funcionalidad mínima: - Lightbox simple para capturas: al hacer clic en una screenshot, mostrarla en overlay a pantalla completa. Clic en overlay = cerrar. - (Opcional) Filtrado de juegos por tags en la home. - No usar frameworks, vanilla JS puro. ``` ### BLOQUE F — Datos de Ejemplo ``` TAREA F1: Crear 2 archivos YAML de ejemplo en src/games/ Archivo 1: src/games/cosmic-escape.yaml - Rellenar con datos ficticios pero realistas - Incluir al menos 3 screenshots, 2 plataformas de descarga - Incluir todos los campos opcionales como ejemplo Archivo 2: src/games/pixel-dungeon.yaml - Rellenar con datos ficticios diferentes - Incluir solo campos obligatorios (ejemplo minimalista) TAREA F2: Crear imágenes placeholder - static/logo/jailgames-logo.png → placeholder SVG o imagen genérica - static/games/cosmic-escape/logo.png → placeholder - static/games/cosmic-escape/screenshot-1.png → placeholder 800x450 - static/games/cosmic-escape/screenshot-2.png → placeholder 800x450 - static/games/pixel-dungeon/logo.png → placeholder (Pueden ser SVGs generados con texto identificativo) TAREA F3: Crear archivos de descarga de ejemplo - downloads/cosmic-escape/README.txt (archivo de texto que diga "Aquí irá el binario de Cosmic Escape") - downloads/pixel-dungeon/README.txt (archivo de texto que diga "Aquí irá el binario de Pixel Dungeon") ``` ### BLOQUE G — Página 404 ``` TAREA G1: Crear src/404.njk Página de error 404: - Layout: base.njk - Permalink: /404.html - Mensaje amigable: "Página no encontrada" - Enlace de vuelta a la home ``` ### BLOQUE H — Docker ``` TAREA H1: Crear Dockerfile en la raíz Multi-stage build: - Etapa 1 (builder): node:20-alpine, npm ci, npx @11ty/eleventy - Etapa 2 (serve): nginx:alpine, copiar _site y downloads, copiar nginx.conf TAREA H2: Crear nginx.conf en la raíz Configuración Nginx: - Puerto 80, root /usr/share/nginx/html - Gzip activado para text/html, text/css, application/javascript - Cache de imágenes 7 días - Descargas con Content-Disposition: attachment - try_files para rutas limpias - error_page 404 → /404.html TAREA H3: Crear docker-compose.yml en la raíz Servicio jailgames: - Build desde el directorio actual - Puerto 80:80 - Volumen downloads como read-only - restart: unless-stopped ``` ### BLOQUE I — Documentación ``` TAREA I1: Crear README.md en la raíz Incluir: - Nombre del proyecto y descripción breve - Requisitos previos (Node.js 18+, Docker) - Cómo instalar dependencias (npm ci) - Cómo ejecutar en desarrollo (npm run serve) - Cómo hacer build (npm run build) - Cómo añadir un juego nuevo (los 7 pasos del pipeline) - Cómo desplegar con Docker (docker compose up --build) - Cómo desplegar sin internet (docker save/load) - Estructura de carpetas resumida ``` ### BLOQUE J — Verificación Final ``` TAREA J1: Ejecutar npm run build y verificar que _site/ se genera correctamente. TAREA J2: Ejecutar npm run serve y verificar en http://localhost:8080: - La home muestra las tarjetas de los 2 juegos de ejemplo - Hacer clic en una tarjeta lleva a la ficha individual - La ficha muestra logo, capturas, descripción y botones de descarga - Los botones de descarga funcionan (descargan el archivo) - El enlace "Volver" funciona - La página 404 funciona al visitar una ruta inexistente - El diseño es responsive (probar en móvil con DevTools) TAREA J3: Ejecutar docker compose up --build y verificar en http://localhost:80 que todo funciona igual que en J2. ``` --- ## Resumen Ejecutivo | Aspecto | Decisión | |---|---| | Generador | Eleventy (11ty) | | Formato de datos | YAML (1 archivo por juego) | | Plantillas | Nunjucks | | CSS | CSS custom con tema oscuro (opcionalmente PicoCSS) | | Servidor | Nginx en Docker | | Build | Multi-stage Docker (Node → Nginx) | | Tiempo para añadir un juego | ~5 minutos (crear YAML + copiar archivos + rebuild) | | Funciona sin internet | Sí (docker save/load) |