Una migración a Tailwind v4 que también requirió parchear un paquete compartido
Este sitio había quedado fijado en Tailwind v3 desde que una actualización agrupada de dependabot intentó subirlo a v4 y rompió el build directamente. La migración real se quedó en el backlog durante un tiempo. Cuando finalmente la retomé, el trabajo de verdad no fue el cambio de configuración, fue encontrar un error que un build limpio nunca me habría mostrado.
La parte mecánica de una migración de Tailwind de v3 a v4 en Astro está bien documentada: cambiar la integración @astrojs/tailwind por el plugin nativo @tailwindcss/vite, y reemplazar @tailwind base/components/utilities por @import "tailwindcss"; en el CSS. La parte que de verdad tuve que verificar era si podía conservar mi tailwind.config.mjs existente en vez de reescribir todo al formato CSS-first @theme de v4. Resultó que sí, mediante una directiva @config, pero no quería simplemente confiar en la documentación para eso. Este archivo de configuración hace dos cosas fundamentales: apunta a contenido que vive dentro de paquetes node_modules/@vdaluz/*, y define colores usando la sintaxis de placeholder rgb(var(--x) / <alpha-value>). Que cualquiera de las dos se rompiera en silencio sería invisible en un log de build.
Ya me había quemado antes con exactamente este tipo de rotura silenciosa (un glob de contenido de Tailwind que se saltó un paquete y simplemente nunca generó las clases que necesitaba, sin ningún error en ningún lado). Así que en vez de hacer el build y darlo por terminado, volví a correr esa misma comprobación de regresión: cargar la página de privacidad, tomar los enlaces compartidos de PrivacyExplainer, y revisar su text-decoration-line calculado. Seguía siendo underline. El glob de contenido de node_modules sobrevivió la migración. Misma historia para los colores de valor alfa, revisé un valor real calculado de box-shadow/color en el navegador en vez de asumir que la sintaxis de placeholder seguía funcionando en v4.
Lo que no esperaba era encontrar un error real en un repositorio completamente distinto. Tailwind v4 renombró o eliminó un puñado de nombres de utilidad “pelados”, shadow y flex-grow sencillos entre ellos. Grepeé mi propio markup buscando esos nombres antes de empezar (encontré uno: una clase rounded pelada que había quedado en la página 404, fácil de arreglar). Pero también grepeé el paquete vendorizado @vdaluz/astro-blog que vive en node_modules, ya que sus componentes se renderizan en cada post de este blog. Su componente RelatedPosts.astro usaba tanto shadow como flex-grow. Como esa es una dependencia de tarball fijada, no código que pueda editar dentro de este repo, eso habría salido con sombras faltantes en silencio y un layout flex roto en cada tarjeta de posts relacionados. Sin error de build, sin warning. Solo una tarjeta con un aspecto un poco peor hasta que a alguien se le ocurriera notarlo.
Sin embargo, corregirlo no fue tan simple como un renombrado de buscar y reemplazar. Ese mismo paquete también sirve a vdaluz.com, que todavía está en Tailwind v3. Y v4 no solo eliminó shadow, también renombró shadow-sm para que significara lo que antes significaba el shadow pelado de v3. Así que cambiar shadow por shadow-sm en el paquete compartido también habría cambiado el peso visual de vdaluz.com, como efecto secundario de una migración que no tiene nada que ver con vdaluz.com. Terminé escribiendo la sombra como un valor arbitrario explícito, el CSS literal que produce el shadow por defecto de Tailwind v3, para que se renderizara byte a byte idéntico en ambas versiones. flex-grow fue más simple: grow ha sido un alias seguro desde Tailwind v3.3, sin diferencia visual en ninguna de las dos versiones. Se publicó como astro-blog v0.6.2, probado mediante el propio proceso de release del paquete (pack, instalar en un consumidor real, check y build) antes de apuntar la dependencia de este repo a la nueva tag.
La lección que se me va a quedar: el radio de impacto de un bump de dependencia no está limitado a la carpeta src/ propia. Si se consumen paquetes compartidos, el markup que importa es el que está realmente instalado en node_modules, y vale la pena grepear también ahí antes de confiar en un build limpio.
Lecturas relacionadas
Un error que se arregló solo, y un botón que por fin encajó
Dos hallazgos pequeños de una revisión de UI/UX tomados del backlog: uno ya se había arreglado por un cambio sin relación, el otro fue una corrección real de una línea en un botón.
Adoptar un componente compartido sacó a la luz un error que el componente no tenía
Una página de política de privacidad de una biblioteca compartida se renderizó perfecto en cada verificación que hice, excepto en la única que realmente importaba.
Los pills de filtro existían, solo que no donde alguien pudiera encontrarlos
Vistas filtradas por proyecto con una linda barra de navegación de pills, accesibles solo desde las tarjetas de la portada. El índice principal /blog, donde realmente aterriza todo el mundo, no tenía ninguna forma de entrar.