Dos pilares: un recorrido guiado y una referencia consultable
Frontend Classroom es una plataforma web para aprender HTML y CSS desde cero. Se apoya en dos pilares que funcionan por separado: el Recorrido, 29 módulos con 87 lecciones y un sandbox ejecutable en cada ejercicio; y la Referencia, 134 entradas consultables al estilo MDN, que no dependen de haber hecho el recorrido. Hay además un glosario.
Es un proyecto 100% propio: 159 commits, todos míos, sin upstream que acreditar. Está construido con Astro 5 (sitio estático), React 19 sólo donde hace falta interactividad — el sandbox es la única isla —, TypeScript y Tailwind CSS 4.
El repositorio es privado y no hay botón Repo. Es una decisión, no una carencia: lo que se puede revisar es la demo desplegada y las capturas de esta página, todas sacadas de la app real corriendo. Este caso se apoya en eso a propósito.
Un validador que mira el DOM real, no el texto que escribiste
Es la pieza más técnica del proyecto y la razón por la que la plataforma no es un blog con ejercicios. Cada lección con ejercicio trae un editor CodeMirror 6 embebido — pestañas HTML y CSS — con preview en vivo, y un botón Comprobar. Al apretarlo corre un set de checks definido en el propio contenido de la lección contra el DOM ya renderizado.
La diferencia importa. Validar el texto escrito obliga a esperar una respuesta exacta y castiga al alumno
por poner un espacio de más, por usar comillas simples o por anidar distinto. Validar el DOM renderizado
pregunta lo que de verdad importa —¿existe un h1? ¿existe un p?— y acepta cualquier forma de escribir
que produzca ese resultado.

El motor vive en src/lib/sandbox-runtime.ts, fuera de los componentes, y los tipos de check están
declarados en src/content.config.ts — o sea que el schema de contenido valida en build qué checks puede
pedir una lección. Es el mismo criterio que aplico en el resto del proyecto: la lógica de dominio vive en
src/lib/ (referencia-utils.ts, recorrido-secciones.ts), separada de la vista que la consume.
El costo de esta decisión es que el feedback no puede ser tan específico como el de un validador de texto:
el motor sabe que falta un p, no sabe si el alumno lo escribió mal o directamente no lo intentó. A
cambio, nunca rechaza una solución correcta escrita distinto — que es el error que más rápido hace
abandonar a alguien que recién empieza.
29 módulos, 87 lecciones, en orden
El Recorrido es el camino guiado: 29 módulos que van de cómo funciona la web hasta construir interfaces,
organizados en dos capas —HTML para estructurar, CSS para dar forma— y 87 lecciones en total. Los módulos
son JSON y las lecciones Markdown, dos colecciones de contenido separadas bajo src/content/.

Cada lección tiene su propio layout (TrackLayout) con el índice del módulo a la izquierda, el contenido
al centro y el TOC de la lección a la derecha; el sandbox aparece embebido en las lecciones marcadas como
ejercicio. Las capas de layout están separadas por tipo de página —PageLayout de base, más TrackLayout,
RefLayout y MarketingLayout—, así que el recorrido y la referencia pueden tener navegación distinta sin
pelearse por un layout común.
134 entradas consultables, autónomas del recorrido
La Referencia es el segundo pilar y funciona sola: 134 entradas de HTML y CSS agrupadas por capa y categoría, con buscador y un modo A-Z alternativo. No hay que haber hecho el recorrido para usarla.

Cada entrada es una página completa, no una definición de una línea: tiene su propio TOC, badges de tipo (concepto / elemento) y de vigencia, breadcrumb, y secciones fijas — cómo funciona, ejemplo realista, cuándo usar qué, notas y gotchas, vigente o legacy.

Que la referencia sea autónoma es lo que hace que la plataforma siga sirviendo después de terminar el recorrido — el momento en el que un tutorial normal deja de tener uso.
El contenido también se testea
Linter estructural de lecciones
lecciones-linter.test.ts recorre los 29 módulos y valida que la estructura del contenido sea la esperada. Un módulo mal armado rompe el test, no la clase.
Tests de ejecución
lecciones-ejecucion.test.ts comprueba que las lecciones corran de verdad, no solo que estén bien escritas.
El motor de checks, testeado aparte
sandbox-runtime.test.ts cubre el validador por sí solo — es lógica pura extraída de la vista, así que se puede testear sin navegador.
End-to-end con Playwright
Playwright configurado como suite e2e (test:e2e), sobre la app navegada de punta a punta.
Todo eso corre en un único workflow, ci.yml, en cada push y cada PR a master y dev:
npm ci → npm run build → npx astro check → npm test → node verify-referencia.mjs. El último paso
es un script propio, no genérico: verifica la referencia, que es la colección más grande y la que más
fácil se desincroniza.
El README lo deja escrito como regla, no como sugerencia: “Gate de calidad antes de mergear: npm run build y npx astro check en verde, más el harness de lecciones cuando se toca contenido.” Testear el
contenido igual que el código es la parte que no suele estar en un proyecto educativo, y acá es la que más
trabajo ahorró: 87 lecciones se rompen en silencio mucho más fácil que 87 funciones.
Cerrado como versión, con una licencia por cada mitad
Dos licencias, porque son dos cosas distintas
El código (Astro, TypeScript, componentes, scripts, config, estilos) va bajo MIT. El contenido educativo (las lecciones, la referencia y el glosario, todo lo que vive en src/content/) va bajo CC BY 4.0, con archivos de licencia separados (LICENSE y LICENSE-CONTENT). Cubrir 87 lecciones escritas a mano con una licencia de software hubiera sido cómodo y equivocado: no son software, y las condiciones que quiero para reutilizarlas no son las mismas.
Privado, y por eso sin botón Repo
El repositorio es privado, así que este caso no puede ofrecer «mirá el código». La consecuencia la asumo: la evidencia son la demo desplegada y las capturas de la app real que hay en esta página, no un enlace a GitHub.
El proyecto está finalizado: es una versión cerrada, con los dos pilares completos y poblados con contenido real, no un esqueleto a medio llenar. El último push es del 2026-07-01 y eso es lo que parece — un cierre ordenado, no una pausa.
