Saltar al contenido principal
Volver atrás

FOUC en Astro: Guía Definitiva, Parte I (Script + Config)

#astro #javascript

El FOUC (Flash of Unstyled Content) es uno de los problemas que más me ha cstado de arreglar siempre. Es un dolor de cabeza. Ahora, en este proyecto me encontré con dos niveles de FOUC, y aquí explico cómo solucioné ambos.

Nivel 1: El Parpadeo de Color (The White Flash)

El navegador pinta la página en blanco por defecto antes de cargar los estilos oscuros. Solución: Inyectar un script “bloqueante” en el <head> del Layout principal.

Hay que tener en cuenta que esto además forma parte de la lógica para controlar entre temas claro y oscuro al iniciar el sitio.

html
<head>
<script is:inline>
  (function() {
    const theme = localStorage.getItem('theme');
    const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
    
    if (theme === 'dark' || (!theme && systemDark)) {
      document.documentElement.classList.add('dark');
      // Inyección directa de estilos para inmediatez absoluta
      document.documentElement.style.backgroundColor = '#131313';
      document.documentElement.style.colorScheme = 'dark';
    } else {
      document.documentElement.classList.remove('dark');
      document.documentElement.style.backgroundColor = '#ffffff';
       document.documentElement.style.colorScheme = 'light';
    }
  })();
</script>
</head>

Nivel 2: El Salto de Layout (CSS Diferido)

A pesar del script, al navegar entre páginas, los elementos se “desmontaban” por un instante. El culpable: Un plugin de optimización (@playform/inline) mal configurado.

Este plugin inlines el CSS crítico, pero si se coloca antes de otros procesos de build en astro.config.mjs, puede causar que el CSS se cargue de forma asíncrona incorrecta, provocando saltos. O al menos eso me dijo Gemini 3.

Solución: Mover los plugins de optimización al final del array integrations o… lo que realmente me solucionó el problema, cargármelo.

javascript
export default defineConfig({
// MAL: inline() antes de mdx()
// integrations: [tailwind(), react(), inline(), mdx()],

// BIEN: Optimizaciones al final (o deshabilitadas si dan problemas)
integrations: [tailwind(), react(), mdx() /*, inline() */],
});

Esta integración inyecta CSS en el HTML, debería de servir para optimizar la carga, pero al menos en mi caso fue a costa de añadirme ese FOUC. Por ahora su eliminiación no me ha afectado en nada aparente.

Lección: La estabilidad visual (CLS) es más importante que ahorrar 2ms de carga. Si una optimización rompe la experiencia, fuera.