Sitio estático generado con Eleventy (11ty) + Nginx en Docker: - Plantillas Nunjucks con layout base, tarjetas y fichas individuales - Datos de juegos en YAML, colección ordenada por fecha - CSS con tema oscuro gaming y diseño responsive (3/2/1 columnas) - Lightbox vanilla JS para capturas de pantalla - Build multi-stage Docker (node:20-alpine → nginx:alpine) - 2 juegos de ejemplo con imágenes SVG placeholder Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
29 KiB
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/<slug>.yaml |
| Imágenes de cada juego | static/games/<slug>/ |
| Binarios descargables | downloads/<slug>/ |
| 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
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)
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.
---
layout: base.njk
title: "JAILGAMES — Nuestros Juegos"
---
<section class="hero">
<img src="/logo/jailgames-logo.png" alt="JAILGAMES" class="site-logo" />
<p class="hero-subtitle">Juegos caseros, hechos con cariño</p>
</section>
<section class="games-grid">
{% for game in collections.games %}
{% include "game-card.njk" %}
{% endfor %}
</section>
4.3 Tarjeta de Juego (src/_includes/game-card.njk)
<a href="/game/{{ game.slug }}/" class="game-card">
<div class="game-card__image">
{% if game.logo %}
<img src="{{ game.logo }}" alt="{{ game.name }} logo" loading="lazy" />
{% else %}
<div class="game-card__placeholder">🎮</div>
{% endif %}
</div>
<div class="game-card__info">
<h2 class="game-card__title">{{ game.name }}</h2>
{% if game.tagline %}
<p class="game-card__tagline">{{ game.tagline }}</p>
{% endif %}
<div class="game-card__meta">
<span class="game-card__version">v{{ game.version }}</span>
{% if game.tags %}
{% for tag in game.tags %}
<span class="game-card__tag">{{ tag }}</span>
{% endfor %}
{% endif %}
</div>
</div>
</a>
4.4 Generación de Fichas Individuales (src/game.njk)
Eleventy genera una página por cada archivo YAML mediante paginación:
---
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)
{% extends "base.njk" %}
{% block content %}
<article class="game-detail">
<header class="game-detail__header">
{% if game.logo %}
<img src="{{ game.logo }}" alt="{{ game.name }}" class="game-detail__logo" />
{% endif %}
<div>
<h1>{{ game.name }}</h1>
{% if game.tagline %}<p class="game-detail__tagline">{{ game.tagline }}</p>{% endif %}
<div class="game-detail__meta">
<span>v{{ game.version }}</span>
<span>{{ game.release_date | dateFormat }}</span>
{% if game.genre %}<span>{{ game.genre }}</span>{% endif %}
{% if game.players %}<span>{{ game.players }}</span>{% endif %}
{% if game.engine %}<span>{{ game.engine }}</span>{% endif %}
</div>
</div>
</header>
<section class="game-detail__screenshots">
{% for img in game.screenshots %}
<img src="{{ img }}" alt="Captura de {{ game.name }}" loading="lazy"
class="game-detail__screenshot" />
{% endfor %}
</section>
<section class="game-detail__description">
{{ game.description | markdown | safe }}
</section>
<section class="game-detail__downloads">
<h2>Descargar</h2>
<div class="download-buttons">
{% for dl in game.downloads %}
<a href="{{ dl.file }}" class="download-btn" download>
<span class="download-btn__platform">{{ dl.platform }}</span>
<span class="download-btn__size">{{ dl.size }}</span>
</a>
{% endfor %}
</div>
</section>
{% if game.tags %}
<section class="game-detail__tags">
{% for tag in game.tags %}
<span class="tag">{{ tag }}</span>
{% endfor %}
</section>
{% endif %}
<a href="/" class="back-link">← Volver al catálogo</a>
</article>
{% endblock %}
4.6 Descargas Locales
Las descargas se enlazan directamente a archivos en /downloads/<slug>/. El atributo download en el <a> 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
: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/<slug>.yaml
(copiar de un juego existente y modificar campos)
Paso 2 Crear carpeta de imágenes
→ static/games/<slug>/
Copiar: logo.png, screenshot-1.png, screenshot-2.png, ...
Paso 3 Crear carpeta de descargas
→ downloads/<slug>/
Copiar binarios: <slug>-v<version>-<platform>.<ext>
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/<slug>.yaml
Paso 2 Si hay imágenes nuevas, copiarlas a static/games/<slug>/
Paso 3 Si hay binarios nuevos, copiarlos a downloads/<slug>/
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
# ---------- 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
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
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:
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> a /game/{{ game.slug }}/
- Imagen del logo del juego (o placeholder si no hay logo)
- Nombre del juego <h2>
- 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/<slug>/ 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) |