Continuación de: «Nuevo look en el blog: descubre la mecánica detrás de mi tema minimalista en WordPress«
En el artículo anterior te conté la filosofía detrás de Jure Minimal Blog: por qué decidí construir mi propio tema desde cero, qué decisiones de diseño tomé y cómo quedó el resultado visual. Si todavía no lo leíste, te recomiendo empezar por ahí para tener el contexto completo.
Este artículo es distinto. Es un dev log puro: la historia de cómo salí a arreglar una cosa, encontré otra, y terminé alcanzando 100/100 en Google PageSpeed casi sin haberlo planeado.
El punto de partida: los comentarios
Todo empezó con la sección de comentarios.
No se veía del todo bien: márgenes desalineados, viñetas fuera de lugar… Mi primera hipótesis fue de rendimiento. Pensé que podía aligerar el CSS extrayendo los estilos específicos de comentarios e inyectándolos inline directamente en comments.php, para que solo se cargaran en las páginas que realmente los necesitan.
Al meterme en el código para hacer eso, descubrí el problema real: mi hoja de estilos seguía apuntando a ol.commentlist (el nombre de clase que usaba WordPress en versiones antiguas). Pero desde hace tiempo WordPress genera el HTML como <ol class="comment-list">, con guion en medio. Un solo carácter de diferencia que rompía todo el estilo de forma silenciosa.
Corregí el selector de inmediato. Y, ya que estaba, implementé igual el CSS condicional, porque en teoría seguía teniendo sentido.
Spoiler: el impacto en rendimiento fue prácticamente nulo. El CSS de comentarios era tan pequeño que no marcaba diferencia medible. Pero esa pequeña búsqueda me hizo fijarme en algo más importante: ¿qué tan pequeño era realmente el CSS principal del tema?
El hallazgo real: 3.3 KiB bloqueando todo
Al revisar los tamaños, vi que style.min.css pesaba apenas 3.3 KiB. Minificado, compacto y limpio. Sin embargo, PageSpeed Insights lo señalaba como el principal cuello de botella del sitio:
Render-blocking requests — Est. savings: 150 ms
style.min.css?ver=1.5.1 3.3 KiB 160 ms
Y en el árbol de dependencias críticas:
Initial Navigation /blog/ 226 ms 9.95 KiB
└── style.min.css?ver=1.5.1 387 ms 3.27 KiB
El navegador no podía pintar ni un solo píxel hasta que recibiera ese archivo. La latencia acumulada llegaba a 387 ms. No por el peso del CSS en sí, sino por el tiempo de red completo: la petición HTTP, la respuesta del servidor y el procesamiento.
Ahí lo vi claro: si el archivo solo pesa 3.3 KiB, no tiene ningún sentido servirlo como recurso externo. Es tan pequeño que se puede inyectar directamente en el sin costo real.
Hice la prueba.
La solución: CSS inline en el <head>
En vez de seguir usando wp_enqueue_style, modifiqué functions.php para que el tema leyera el contenido de style.min.css y lo volcase directamente en el HTML de cada página:
function jure_minimal_blog_inline_styles() {
$css_file = get_template_directory() . '/style.min.css';
if ( file_exists( $css_file ) ) {
echo '<style>' . file_get_contents( $css_file ) . '</style>';
}
}
add_action( 'wp_head', 'jure_minimal_blog_inline_styles' );
Ahora el CSS ya no es una petición de red separada: forma parte del HTML inicial. El navegador tiene todo lo necesario para renderizar desde el primer byte que recibe.
Vi los resultados y decidí dejarlo así.
Un arreglo de consistencia visual (ya que estaba)
Mientras revisaba los estilos, noté que la página 404 tenía botones con bordes rectos (border-radius: 0), colores fijos que ignoraban las variables CSS del tema y animaciones que no existían en ningún otro lugar del blog.
No era el objetivo principal de la sesión, pero era un detalle fácil de arreglar. Unifiqué todo con las mismas variables (--accent-color, border-radius: 3px) y el mismo comportamiento de hover y focus que el resto del tema.
Los estilos de comentarios, más allá del fix del selector, todavía necesitan algunos ajustes. Los dejaré para una próxima revisión.
Los números
Así quedó el antes y el después:
Antes (v1.5.1):
| Problema | Impacto medido |
|---|---|
| CSS bloqueante | 387 ms de latencia crítica |
| Ahorro estimado PageSpeed | 150 ms |
| Tamaño del recurso | 3.3 KiB |
Después (v1.5.2):
| Métrica PageSpeed | Puntuación |
|---|---|
| Rendimiento | 100 |
| Accesibilidad | 100 |
| Mejores prácticas | 100 |
| SEO | 100 |
100/100 en las cuatro categorías. Una de esas metas que persigues sin saber muy bien si la vas a alcanzar, y cuando llega te da una satisfacción desproporcionada.
Una nota sobre el CSS inline (y lo que revelan los headers)
La pregunta obvia es: ¿no es mejor que el navegador descargue el CSS una vez y lo guarde en caché?
En teoría sí. Pero los headers de la respuesta real cuentan otra historia:
Transferred: 9.95 kB (32.83 kB size)
content-encoding: zstd
x-fastcgi-cache: HIT
x-cache-status: HIT
cache-control: public, max-age=7200
cf-cache-status: DYNAMIC
Tres puntos clave:
- El HTML completo (con el CSS inline) viaja comprimido con zstd y pesa menos de 10 KiB transferidos. Cloudflare comprime todo junto, así que el inline no engorda la página de forma preocupante.
x-fastcgi-cache: HIT. El servidor tiene caché de página completa activa. El HTML con el CSS incluido ya está precalculado y listo para servirse sin ejecutar PHP ni WordPress en cada petición. No hay penalización por “reconstruir” el CSS cada vez.- La respuesta viaja sobre HTTP/3, que reduce significativamente la latencia de conexión.
Todo esto neutraliza bastante el argumento clásico contra el CSS inline («pierdes la caché del navegador»). Cuando tienes una buena capa de caché de servidor y una CDN por delante, el CSS del tema no necesita vivir necesariamente en un archivo separado.
La infraestructura que hace posible todo esto
Estos headers no son solo datos técnicos: son la prueba de varias capas trabajando juntas — FastCGI cache a nivel de servidor, Cloudflare como proxy de borde, HTTP/3, compresión moderna…
Si has leído los artículos anteriores de esta serie (sobre Redis y Memcached como capa de objeto cache, y sobre el servidor web como proxy), este resultado es la demostración práctica de por qué esas capas importan tanto. El CSS inline funciona tan bien precisamente porque hay una infraestructura sólida por encima que lo respalda.
Queda pendiente el artículo sobre la capa más visible de todas: el edge, la CDN, el punto donde la petición del usuario toca el sistema antes de llegar al servidor. Eso viene pronto.
Lo que viene en el tema
El selector de comentarios ya está corregido, pero la sección todavía necesita más trabajo estético. Lo dejaré para la próxima iteración.
Una advertencia importante antes de replicar esto
La técnica del CSS inline funciona muy bien en este contexto específico: un VPS con Nginx configurado como caché de página completa y Cloudflare como capa de borde. Esa combinación neutraliza la pérdida de caché de navegador y hace que el tradeoff sea claramente positivo.
Pero no todos los sitios tienen esa infraestructura. En un hosting compartido estándar, sin caché de servidor ni CDN, el CSS inline podría ser contraproducente: el navegador no puede cachear el CSS entre páginas porque viaja embebido en cada HTML, lo que significa que cada visita descarga los mismos 3.3 KiB de nuevo.
Por eso, en futuras versiones del tema voy a revertir a la carga tradicional con wp_enqueue_style. El objetivo es que Jure Minimal Blog pueda funcionar bien en cualquier entorno, no solo en uno con una configuración particular. Mientras tanto, si quieres replicar el inline en tu propio tema, asegúrate primero de que tienes caché de página activa — ya sea a nivel de servidor, de plugin (como W3 Total Cache o WP Rocket), o de CDN.
El tema completo está disponible en GitHub bajo licencia GPL: github.com/jure-ve/jureminimalblog
Si estás armando tu propio tema en WordPress y quieres llegar a 100/100 en PageSpeed, te recomiendo mirar primero el tamaño real de tu CSS y, sobre todo, qué hay por encima de tu servidor. La combinación de ambas cosas es donde suele estar la diferencia real.
Y si encuentras algo roto, o tienes una idea para mejorar el tema sin perder su esencia minimalista y rápida, abre un issue. Me encantaría recibir feedback y seguir puliendo esto juntos.