La promesa de una tarde productiva

El plan era simple. Tenía tres pantallas diseñadas en Google Stitch para la landing de Pólizas, tokens de diseño ya extraídos, y un pipeline documentado paso a paso. Solo faltaba traducir el HTML de Stitch a componentes Astro. Dos horas, máximo tres.

Fueron ocho.

Y el problema no fue la complejidad del diseño ni una herramienta rota. El problema fue algo mucho más traicionero: las mismas clases CSS resolvían a valores completamente diferentes entre Tailwind v3 y v4.

El contexto: Taste Engine

Taste Engine es el pipeline que estoy construyendo para refinar el diseño visual de mis landing pages. La idea es generar pantallas de referencia en Google Stitch (que usa Tailwind CDN v3), extraer los tokens de diseño, y traducirlos a mi stack de producción en Astro (que usa Tailwind CSS v4 con el nuevo sistema @theme).

El pipeline ya había funcionado para generar las pantallas. La landing de Pólizas tenía tres secciones diseñadas: hero, pasos del proceso y una sección de preguntas frecuentes. Todo se veía perfecto en Stitch.

Pero cuando renderizé el mismo HTML en Astro, la landing parecía otra cosa. Los títulos estaban delgados. Los círculos de los pasos eran rectángulos. El subheadline se renderizaba palabra por palabra en una columna absurdamente angosta. Y los espaciados entre secciones habían desaparecido por completo.

Los cuatro gotchas

Lo que siguió fue un proceso de debugging metódico que reveló cuatro incompatibilidades silenciosas entre TW3 y TW4. Ninguna lanzó un error. Ninguna apareció en la consola. Todas rompieron cosas.

1. --spacing-2xl secuestra max-w-2xl

En Tailwind v4, cuando defines --spacing-2xl: 80px en tu bloque @theme, esa variable no solo afecta a las clases de spacing como p-2xl o gap-2xl. También secuestra max-w-2xl, que en v3 siempre resolvía a 42rem (672px).

El resultado: mi subheadline tenía max-w-2xl y se renderizaba en una columna de 80 pixeles de ancho. Literalmente una palabra por línea. Lo más insidioso es que la clase se “aplica correctamente” — solo que su valor es absurdo.

2. --radius-full rompe los círculos

En mi @theme tenía definido --radius-full: 0.75rem. Parecía inofensivo. Pero en TW4, el valor default de rounded-full es calc(infinity * 1px) — una forma elegante de garantizar círculos perfectos independiente del tamaño del elemento.

Al sobreescribirlo con 0.75rem, los step circles de la sección de proceso pasaron de ser círculos a ser rectángulos con esquinas redondeadas. La diferencia es sutil en elementos pequeños, pero obvia cuando el diseño depende de la geometría.

3. fontWeight desaparece

En Tailwind v3, las clases tipográficas como text-h1 que vienen de plugins o configuraciones custom suelen incluir font-size, line-height y font-weight empaquetados. En v4, los tokens --text-* solo proveen font-size y line-height.

Todos mis headings se renderizaban en weight 400 (regular) en vez de 700 (bold). Los títulos estaban ahí, del tamaño correcto, pero se veían débiles, sin presencia. No era un bug, era una omisión de diseño que solo se nota cuando comparas lado a lado.

4. La causa raíz: CSS @layer cascade

Este fue el descubrimiento que lo cambió todo.

En mi archivo landing.css tenía un reset global estándar:

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

Parece inocente. Pero estaba escrito fuera de cualquier @layer. En el modelo de cascade layers de CSS (que TW4 usa de forma nativa), los estilos sin layer tienen prioridad absoluta sobre cualquier estilo dentro de un layer, incluido @layer utilities.

Esto significa que * { margin: 0; padding: 0; } estaba anulando silenciosamente todas las utilidades de spacing de Tailwind: mb-32, pt-32, mt-40, py-24. Todas. Sin excepción. Sin ningún error visible.

La sección de FAQ que debía tener 128px de separación superior estaba pegada al contenido anterior. Todas las secciones estaban colapsadas como un acordeón cerrado.

Cómo lo descubrimos

La metodología que funcionó fue iterativa pero deliberada:

Primero, generé un HTML combinado con las tres pantallas de Stitch como referencia visual side-by-side. Poder comparar en la misma ventana del navegador fue fundamental.

Segundo, usé la técnica de “debug borders”: bordes de colores distintos en cada sección (border: 2px solid red, border: 2px solid blue) para visualizar exactamente dónde se perdía el spacing. Cuando una sección tiene pt-32 pero cero pixeles de padding, el borde te lo muestra sin ambigüedad.

Tercero, el test definitivo: reemplazé las clases de Tailwind por inline styles con valores en pixeles (style="padding-top: 128px"). Funcionaban. Las clases equivalentes no. Eso descartó cualquier problema de HTML o JavaScript y apuntó directamente a la cascada CSS.

La solución fue mover todos los estilos base a @layer base {}:

@layer base {
  * {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
  }
}

Con ese cambio, los spacing de Tailwind volvieron a funcionar instantáneamente. Los 128 pixeles de padding aparecieron como si siempre hubieran estado ahí. Porque estaban — solo que eran invisibles.

Lo que aprendí

Hay una lección superficial y una profunda.

La superficial es técnica: si estás migrando de Tailwind v3 a v4, no copies clases. Audita cada token de tu @theme contra los defaults de v4, y asegúrate de que tus estilos globales estén dentro de un @layer.

La profunda es sobre procesos. Taste Engine necesitaba una fase que no existía: una auditoría visual formal entre el diseño de referencia y la implementación en producción. No un vistazo rápido. Un diff visual, clase por clase, sección por sección. Hoy agregué esa fase al skill (Fase 5.5) junto con un checklist de traducción TW3 a TW4 y la técnica de debug borders como herramienta estándar.

Lo que viene

La landing de Pólizas ahora se ve idéntica al diseño de Stitch. Pixel por pixel. Pero quedan tres landings más por pasar por el pipeline refinado: LabelLoop, Ledger y PAIP. Con las lecciones de hoy, esas traducciones deberían ser más rápidas.

El Taste Engine no es solo un pipeline de diseño. Es un sistema de aprendizaje que se fortalece con cada error. Hoy se hizo más robusto porque falló de cuatro formas diferentes, y ahora sabe exactamente cómo evitar cada una.

Eso es Build in Public de verdad. No es mostrar el resultado pulido. Es mostrar las ocho horas de debugging que hacen posible que el resultado exista.