Parte 2 de 8
LeXi: esqueleto Astro + Svelte y sistema de temas
Montar el proyecto Astro a mano, añadir Svelte 5, Cloudflare Pages con wrangler.json y un selector de temas por CSS custom properties.
En el Hito 1 montamos el esqueleto del proyecto. En lugar de npm create astro decidí crear el package.json a mano: la carpeta ya tenía READMEs, banners y el AGENTS.md, y quería control total sobre cada dependencia.
Versiones y conflicto de dependencias
El primer escollo fue casar versiones. La combinación que funcionó (y que ya no toqué):
{
"astro": "^5.0.0",
"@astrojs/svelte": "^7.0.5",
"@vite-pwa/astro": "^1.2.0",
"svelte": "^5.1.16",
"wrangler": "^4.0.0"
}
Lección: las versiones más nuevas no siempre casan.
@astrojs/svelte9 exigía Astro 7 y 8 exigía Astro 6; con Astro 5 la versión compatible es la 7.0.5. Buscar una combinación que respete los peer dependencies evita días de dolores de cabeza.
La configuración
astro.config.mjs con salida estática, el dominio real y las integraciones de Svelte y PWA:
export default defineConfig({
output: 'static',
site: 'https://tu-dominio.com',
integrations: [
svelte(),
AstroPWA({
registerType: 'autoUpdate',
manifest: { /* nombre, iconos 192/512/maskable, theme_color… */ },
workbox: { /* precache + runtimeCaching para static.arasaac.org */ }
})
]
});
Y wrangler.json con la configuración de Pages: salida dist, compatibility_date, flag nodejs_compat y los bindings que iremos añadiendo.
Lección:
@vite-pwa/astrono exportaVitePWA. Se configura con el default export de la integración, no con el plugin de Vite. Otro detalle: los campos dewrangler.jsoncambian entre versiones;pages_projectno es válido ymigrations_dirva dentro de cada entrada ded1_databases.
El sistema de temas
Lo definí con CSS custom properties y un atributo [data-theme] en <html>, sin tocar componentes para añadir un tema nuevo:
:root {
--radius: 12px;
--tile-gap: 0.75rem;
}
[data-theme="neutral"] { --primary: #2563eb; --surface: #fff; /* … */ }
[data-theme="pastel"] { /* … */ }
[data-theme="high-contrast"] { /* … */ }
[data-theme="calm"] { /* … */ }
[data-theme="warm"] { /* … */ }
El ThemeSelector.svelte aplica el tema y lo persiste en localStorage (lexi:theme), y un script inline en Layout.astro restaura el tema guardado antes de hidratar para evitar el parpadeo.
Lección:
$stateen Svelte 5.56 no acepta un inicializador de función.$state<string>(() => localStorage.getItem(...))da error de tipos; hay que inicializar a un valor y leerlocalStorageenonMount(además es más seguro para SSR).
Iconos y seguridad
scripts/generate-icons.mjsgenera consharplos PNG 192/512/maskable a partir delfavicon.svg.public/_headersdefine la CSP y políticas de seguridad:Permissions-Policy: microphone=(self), camera=(self).script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'— el'unsafe-inline'es imprescindible porque Astro hidrata las islas con scripts inline.
Algo que aprendimos por las malas: sin
'unsafe-inline'enscript-src, el navegador bloquea la hidratación de las islas y todo se queda en “Cargando”. Lo sufrimos en el Hito 5; lo cuento ahí con detalle.
Verificación y cierre
npm install,npm run check(0 errores),astro buildOK.- Preview local: HTTP 200,
data-theme="neutral"en el HTML, isla hidratada,sw.js+manifest.webmanifestgenerados. - Commit
chore: scaffold Astro + Svelte + Pages/PWA config + theme system+ backuplexi-$ts-Hito1.zip.
También dejé preparado el flujo de git: rama main, remote origin, y los mensajes de commit siempre en inglés con Conventional Commits (convención que fijamos en AGENTS.md).
En la siguiente parte, conectamos la base de datos D1.