< BLOG />

Tailwind: esto no es Bootstrap

Entiende qué genera tu IA cuando utiliza Tailwind, qué problemas resuelve y cuándo realmente encaja en tu proyecto

< INICIO />

Intro

Llevas un tiempo viendo demos, plantillas y tutoriales montados sobre Tailwind.

Y hay más: si le pides código a tu herramienta de IA favorita, la cabra tira al monte. En cuanto te descuidas, te genera Tailwind sin preguntarte. A veces incluso librerías montadas encima, como shadcn/ui.

Ahí es fácil quedarse tranquilo y pensar: "vale, esto será como el Bootstrap que usábamos hace años".

Pues no. Es un enfoque radicalmente distinto. Y puede que ni siquiera encaje en tu proyecto.

Mi consejo de siempre: tirar de IA está muy bien, pero tienes que saber qué está creando por debajo. Que la IA escriba el código no te libra de saber qué hace. Al contrario: te obliga a entenderlo mejor, porque tú eres quien la tiene que guiar y quien mantendrá ese código dentro de dos años.

Qué vas a encontrar aquí

Esto no es un tutorial. Es una explicación de los fundamentos de Tailwind:

  • Qué es y cómo funciona.
  • Qué problemas resuelve.
  • Qué problemas crea.
  • En qué se diferencia de lo que ya conocías.

El objetivo: que cuando tu asistente de IA te escupa un <div> con quince clases raras, sepas leerlo, corregirlo y decidir si ese camino te conviene.

Parte 1: Un poco de historia

En resumen Bootstrap arrasó entre 2013 y 2016 porque te daba componentes ya diseñados. Cayó porque todas las webs se parecían y porque el CSS moderno lo hizo prescindible. Y aunque era un framework... siempre acababas escribiendo CSS propio a mano.

Para entender Tailwind vamos a hacer una comparación odiosa. Retrocedamos en el tiempo.

Los más viejos del lugar vivimos el auge y la caída de un framework CSS que llegó a dominarlo todo: Bootstrap.

Ojo: comparar Tailwind con Bootstrap es un poco como comparar un huevo con una castaña. Ya veremos por qué. Pero es la comparación que todo el mundo tiene en la cabeza, así que la vamos a usar a nuestro favor.

Maquetar en 2011 era un deporte de riesgo

Situémonos. Estamos en 2011 y maquetar una web era un dolor de... cabeza :P

El panorama era este:

Navegadores que no se actualizaban. Recuerda el sudor frío cuando un cliente te decía "tiene que funcionar en Internet Explorer 7". O peor, en el 6. No existían los navegadores evergreen. La versión instalada era la que había, y podía tener años.

Cada navegador interpretaba los estándares a su manera. Escribías tu CSS, lo probabas en Firefox, abrías IE y... sorpresa. Media jornada se te iba en hacks y hojas de estilos condicionales.

Llegaron los smartphones y las tablets. El iPhone estaba en el mercado desde 2007 y el iPad desde 2010. De repente tu web tenía que verse bien en un montón de resoluciones. El responsive design (término acuñado por Ethan Marcotte en 2010) era la nueva frontera. Hacerlo a mano con media queries era territorio de pioneros.

En ese contexto, dos ingenieros de Twitter liberaron en agosto de 2011 un framework interno: Bootstrap. Se llamaban Mark Otto y Jacob Thornton.

El ascenso: la edad de oro

Bootstrap fue un pelotazo inmediato. Llegó a ser el repositorio con más estrellas de GitHub.

Y no fue casualidad. Resolvía de golpe todos los dolores de la época:

  • Un sistema de grid para hacer layouts responsive sin pelearte con los navegadores.
  • Componentes predefinidos con buena pinta: botones, formularios, modales, navbars, alertas.
  • Snippets por todas partes. Como los componentes eran HTML con clases conocidas, proliferaron sitios llenos de trozos de código listos para copiar y pegar. Funcionaban tal cual. El "vibe coding" de la época, pero a mano :P

La evolución fue rápida:

Fecha Qué pasó
Agosto 2011 Nace Bootstrap
Enero 2012 Bootstrap 2. Añade soporte responsive (todavía como hoja de estilos opcional)
2012-2013 Aparecen librerías de componentes encima, como UI Bootstrap para AngularJS
Agosto 2013 Bootstrap 3. Grid más potente y mobile first de serie

Bootstrap 3 rompía con la versión 2. Todavía conozco sitios que no llegaron a migrar :P

La versión 3, traía un cambio de filosofía importante: mobile first. Se diseña primero para móvil y se escala hacia arriba, no al revés.

Entre 2013 y 2016 vivimos su edad de oro. Si montabas un panel de administración, una web corporativa o un MVP, la pregunta no era "¿uso Bootstrap?". Era "¿qué tema de Bootstrap uso?".

Verlo para creerlo

Vamos a ver un ejemplo típico de la época, con sintaxis de Bootstrap 3.

Una página con contenido principal, barra lateral y su botón de turno:

<div class="container">
  <div class="row">
    <!-- Contenido principal: 8 de 12 columnas -->
    <div class="col-md-8">
      <h2>Mi artículo</h2>
      <p>Un texto interesantísimo...</p>
      <button class="btn btn-primary btn-lg">Leer más</button>
    </div>
    <!-- Barra lateral: 4 de 12 columnas -->
    <div class="col-md-4">
      <div class="panel panel-default">
        <div class="panel-heading">Relacionados</div>
        <div class="panel-body">Más contenido...</div>
      </div>
    </div>
  </div>
</div>

Y así se veía. Ese aspecto que cualquiera que estuviera en el ajo reconoce a un kilómetro:

El snippet renderizado con Bootstrap 3: artículo con botón azul y panel de relacionados, el inconfundible look de la época

💻 Lo tienes funcionando en la demo 00-bootstrap. Es un HTML plano con el CDN de Bootstrap 3, sin build ni instalación. Así era el "setup" de la época.

La clave: cada clase es un concepto de alto nivel

col-md-8 → el grid dividía la pantalla en 12 columnas imaginarias. Esto significa "a partir de pantalla mediana, ocupa 8 de 12". En móvil, los bloques se apilaban solos. Responsive sin escribir una media query.

btn btn-primary btn-lg → no describe estilos, describe qué es la cosa. Un botón, del color principal, en tamaño grande. Detrás hay decenas de propiedades CSS que Bootstrap ya decidió por ti.

panel → una clase, y tienes una caja con cabecera, borde y sombra.

Es decir: tú hablabas el idioma de los componentes, y Bootstrap lo traducía a CSS.

Guárdate este snippet en la retina. Cuando veamos el equivalente en Tailwind, el contraste va a ser muy revelador.

Y aun así, acababas escribiendo CSS a mano

Que Bootstrap te diera todo eso no significa que te libraras del CSS.

En cualquier proyecto real, junto al bootstrap.css convivía un styles.css propio que engordaba con el tiempo.

¿Por qué?

1. Bootstrap cubría el 80% común, no tu 20% particular. Botones, formularios y grids sí. Pero esa cabecera con foto de fondo, ese layout que pedía diseño o ese widget de tu negocio, no. Eso era CSS tuyo.

2. Personalizar tenía trampa. La vía oficial era recompilar Bootstrap tocando sus variables Less o Sass. Pero en muchos proyectos nadie montaba ese pipeline. Se enlazaba el CSS ya compilado desde un CDN y a correr.

Así que la personalización real se hacía sobrescribiendo estilos: selectores cada vez más específicos y, al final,utilizando el temido !important.

3. Ese CSS custom era de "solo añadir". ¿Quién borraba una regla de styles.css? Nadie. No fuera a romperse algo en una página que ya ni recordabas. El fichero solo crecía. Con los años era una zona arqueológica que daba miedo tocar.

Un detalle irónico

Con Bootstrap ya llenábamos los tags de clases: class="col-md-8", class="btn btn-primary btn-lg".

Eso de "el HTML lleno de clases" que hoy se le critica a Tailwind no es nada nuevo. Lo hacíamos con alegría en 2013.

La diferencia está en el nivel de abstracción:

  • Las clases de Bootstrap dicen qué es cada cosa.
  • Las de Tailwind dicen qué estilos aplican.

Es una diferencia profunda. Paciencia, que llegamos.

La caída: el olor a Bootstrap

¿Qué pasó después? Varias cosas a la vez.

El "olor a Bootstrap". Había tantas aplicaciones hechas con él que el aspecto era inconfundible. El mismo navbar, los mismos botones, las mismas cards. A los clientes no les hacía gracia: querían personalidad propia, no otra web clonada.

Los navegadores maduraron. Se volvieron evergreen (se actualizan sólos, no más infierno de mantener versiones antiguas). Los estándares se empezaron a cumplir de verdad. Llegaron Flexbox y CSS Grid nativos. Buena parte de lo que Bootstrap resolvía ya lo resolvía el propio CSS.

Las roturas entre versiones hicieron pupa. El salto de la 2 a la 3 dolió. Pero la versión 4 fue un parto interminable: primera alpha en agosto de 2015, versión estable en enero de 2018. Casi dos años y medio en los que la comunidad se fue buscando la vida por otros lados.

¿Está muerto Bootstrap? No exactamente. En mayo de 2021 salió la versión 5, que por fin se deshizo de jQuery, y sigue teniendo su hueco.

Pero para entonces el centro de gravedad del frontend se había movido hacia:

  • Frameworks basados en componentes (React, Vue, Angular).
  • Design systems propios, documentados y versionados.
  • Tailwind CSS.
  • Librerías de componentes como Material UI o shadcn/ui.

Recapitulando: ¿qué era Bootstrap?

Antes de saltar a Tailwind, quédate con esta foto:

  • Un sistema de grid responsive.
  • Componentes prefabricados con su estilo ya decidido.
  • Un puñado de utilidades CSS para márgenes, colores, tipografía.
  • Temas personalizables con variables Less o Sass.
  • Documentación extensa y comunidad enorme.

En una frase: un fichero CSS que te descargabas con las decisiones de diseño ya tomadas.

Tú ponías el HTML. Bootstrap ponía el criterio estético. Y para lo que se salía del guion, un styles.css propio que crecía y crecía.

Tailwind parte del punto contrario: te da las piezas, pero las decisiones de diseño las tomas tú.

Parte 2: Qué es Tailwind

En resumen Tailwind te da clases minúsculas que hacen una sola cosa cada una. No trae ningún componente hecho: el diseño lo pones tú. No es un fichero CSS que enlazas, es un compilador que genera solo lo que usas.

En pocas palabras

Tailwind es un framework CSS utility-first.

Traducido: en lugar de clases de alto nivel que representan componentes (btn, panel, navbar), te da cientos de clases minúsculas. Cada una hace una sola cosa.

  • flex → activa flexbox
  • p-4 → pone un padding
  • text-white → pone el texto en blanco
  • rounded-lg → redondea las esquinas

Piezas de Lego, en vez de la nave espacial ya montada.

El contraste, en dos líneas de código

¿Te acuerdas del botón de Bootstrap?

<!-- Bootstrap: la clase dice QUÉ ES -->
<button class="btn btn-primary btn-lg">Leer más</button>
<!-- Tailwind: las clases dicen QUÉ ESTILOS APLICAN -->
<button
  class="bg-blue-600 hover:bg-blue-700 text-white text-lg font-semibold px-6 py-3 rounded-lg"
>
  Leer más
</button>

Es brutal, ¿verdad?

En Bootstrap, btn-primary es una caja negra. El aspecto lo decidió el framework.

En Tailwind cada decisión está a la vista: color de fondo, color al pasar el ratón, texto, paddings, borde redondeado. Todo lo decides tú, y todo está escrito ahí mismo.

No, no es anarquía

Tailwind te da un sistema de restricciones:

  • Una escala de espaciados: p-1, p-2, p-4...
  • Una paleta de colores con sus tonalidades: blue-500, blue-600...
  • Una escala tipográfica.

Todo consistente y personalizable.

No te da un diseño hecho. Te da los raíles para construir el tuyo sin que se te vaya de las manos.

Por eso no existe el "olor a Tailwind". Dos webs hechas con Tailwind pueden ser radicalmente distintas.

Y un detalle clave

Tailwind no es un fichero CSS gigante que te descargas, como era Bootstrap.

Es un compilador. Escanea tu código, mira qué clases usas de verdad, y genera un CSS que contiene solo eso. Ni un byte de más.

Volveremos a esto, porque lo cambia todo.

Historia de Tailwind (esta es más corta)

Tailwind nació en 2017, de la mano de Adam Wathan y compañía.

La idea venía de una observación incómoda que él mismo publicó y que generó bastante polémica:

Eso de "separar contenido (HTML) y presentación (CSS)" que nos enseñaron como dogma, en la práctica no escalaba bien.

Ahí está nuestro styles.css arqueológico de la era Bootstrap para atestiguarlo. Y quizá poner las utilidades de estilo directamente en el marcado no era un pecado, sino una solución.

Fecha Versión
Noviembre 2017 Primera versión pública
Mayo 2019 Tailwind 1.0
Diciembre 2021 Tailwind 3
Enero 2025 Tailwind 4

Entre 2019 y 2021, Tailwind pasó de "cosa rara de la que se discute en Twitter" a lo alto de las encuestas tipo State of CSS.

Con una legión de conversos y otra de detractores. Pocas herramientas han generado debates tan encendidos. "¡Eso es CSS inline con guarrerías!" No lo es, y lo iremos viendo.

Tailwind 3: la versión con la que mucha gente lo conoció

Consolidó el motor JIT (Just-In-Time), que compila solo las clases que usas.

Pero la experiencia tenía asperezas. Conviene conocerlas, porque hay muchísimo contenido en internet (y en el entrenamiento de las IAs) que arrastra estos vicios:

El escaneo era textual, no inteligente. Tailwind no ejecuta tu código: busca cadenas que parezcan clases. Si construías una clase dinámicamente, tipo `bg-${color}-500`, el escáner no la veía. Esa clase no se generaba. Y tu botón salía sin fondo en producción, en silencio. Horas de depuración para capturar el gamusino.

Había que configurar a mano qué ficheros escanear. Con la lista content en la configuración. ¿Se te olvidaba una carpeta? Clases desaparecidas, sin previo aviso.

La configuración estaba repartida. Un tailwind.config.js para el tema, un postcss.config.js para el build, tu CSS de entrada con las directivas @tailwind. Piezas dispersas que había que mantener sincronizadas.

Tailwind 4: la versión de hoy

Y aquí la historia tiene su analogía con Bootstrap: una versión mayor que rompe con la anterior.

Los cambios principales:

  • Motor mucho más rápido, con las partes críticas reescritas en Rust.

  • Desaparece el tailwind.config.js. Toda la configuración vive ahora en tu propio CSS, con variables CSS estándar.

  • Detección automática de ficheros. Se acabó mantener la lista content a mano.

El cambio de fondo: las capas nativas

Tailwind 4 se apoya de lleno en el CSS moderno del navegador. El ejemplo estrella son las capas nativas (cascade layers, el @layer estándar).

Ojo, que esto se presta a confusión:

  • En la v3 ya existía una directiva @layer. Pero era un apaño de Tailwind que se resolvía al compilar. El navegador nunca veía capas de verdad.

  • En la v4 el CSS generado usa capas nativas reales: theme, base, components, utilities.

¿Y qué ventajas nos da esto? Dos cosas muy prácticas.

1. La prioridad deja de depender de trucos de especificidad. Pasa a estar ordenada por diseño. Tu CSS propio, al ir fuera de las capas, gana siempre a los estilos del framework. Sin selectores retorcidos ni !important. Justo la guerra que perdíamos una y otra vez con Bootstrap.

2. La convivencia con otras librerías deja de ser un campo de batalla. Mezclar Tailwind con una librería tipo MUI siempre fue terreno pantanoso. El reset de Tailwind y los estilos de la librería se pisaban, y ganaba quien tuviera más especificidad o cargara más tarde. Medio por accidente.

Con capas nativas, cada librería publica sus estilos en su propia capa y tú decides el orden en una línea. De hecho, MUI ya soporta emitir sus estilos en una capa propia para integrarse limpiamente con Tailwind 4.

La cascada deja de ser un campo de batalla y pasa a ser un contrato.

¿Por qué Tailwind 4 sí ha triunfado?

Bootstrap 4 llegó tarde a una comunidad que ya miraba hacia otro lado. Tailwind 4 ha llegado con el viento a favor.

¿Por un aliado inesperado? Las herramientas de IA.

Pasarle contexto es trivial. Todo el sistema de diseño de tu proyecto cabe en un fichero CSS con sus variables. Compara eso con la web clásica: estilos globales por aquí, CSS de componentes por allá, hojas que se pisan, especificidad, orden de importación. Para entender ese sistema hay que leerse media aplicación. Con Tailwind, le pasas un fichero y el componente que toca.

Generar soluciones es igual de fácil. Como los estilos viven en el marcado, la IA genera un componente autocontenido y funciona a la primera. No tiene que decidir en qué fichero CSS meter las reglas, ni inventarse nombres de clases, ni arriesgarse a romper otra pantalla. Cada cambio es local.

Efecto bola de nieve. Hay tantísimo Tailwind en el material de entrenamiento que los modelos lo generan con mucha soltura. Y a su vez, cada proyecto generado con IA produce más Tailwind.

Conclusión: Tailwind 4 es la versión que te vas a encontrar cuando tu asistente se ponga a generar interfaces. Vamos a destriparla.

Parte 3: Instalación

En resumen Tailwind no se enlaza con un <link>: es un compilador y va en tu build. Son tres piezas: el motor, el plugin de tu bundler y un @import en tu CSS. Si un tutorial te manda ejecutar npx tailwindcss init, es de la era v3.

Esto no es un <link> y a correr

Con Bootstrap el setup era enlazar un fichero CSS y listo. La librería era un producto terminado que el navegador consumía tal cual.

Con Tailwind eso no funciona. Ya sabes por qué: Tailwind no es un fichero CSS, es un compilador.

No hay nada que enlazar, porque el CSS de tu proyecto no existe hasta que Tailwind lo genera mirando tu código. Y un compilador hay que engancharlo a tu build.

Las tres piezas

1. El paquete tailwindcss El motor. Escanea tus ficheros buscando clases y genera el CSS.

2. La integración con tu build El motor tiene que ejecutarse cada vez que tocas el código. Según tu stack:

  • El plugin de Vite: @tailwindcss/vite
  • El plugin de PostCSS: @tailwindcss/postcss (para otros bundlers)
  • Una CLI standalone, si no tienes build de JavaScript

Esta pieza hace que, al guardar, el CSS se regenere al vuelo.

3. Tu fichero CSS de entrada Con una línea: @import "tailwindcss";

Este import trae las capas (el tema, el reset, las utilidades). Y es también el sitio donde vivirá toda tu configuración.

Este fichero es el "fichero único de contexto" del que hablábamos antes.

Diagrama comparando ambos enfoques: Bootstrap enlazaba un CSS terminado que el navegador consumía tal cual; en Tailwind 4 tu código y tu index.css entran al motor tailwindcss, enganchado a Vite por el plugin, que genera solo el CSS que usas

⚠️ Fíjate en lo que NO hay

  • Ni tailwind.config.js
  • Ni postcss.config.js (si usas el plugin de Vite)
  • Ni lista content de rutas a escanear

Aviso importante: si un tutorial te manda ejecutar npx tailwindcss init, es de la era v3. Huele a rancio, igual que las webs con olor a Bootstrap.

Esto aplica también a tu asistente de IA. Como hay tantísimo material v3 en su entrenamiento, no es raro que te proponga el setup antiguo.

Si sabes distinguirlos, lo corriges en dos segundos. Si no, acabas con un Frankenstein mitad v3 mitad v4 que te va a pegar petardazos extraños.

Nota práctica: según el framework que uses (Next.js, Astro, SvelteKit, Angular) los pasos varían, y muchos CLIs ya te ofrecen añadir Tailwind al crear el proyecto. Para producción, tirar de esas vías es lo sensato.

Pero merece la pena montarlo a mano al menos una vez. Son cinco minutos, y es la diferencia entre "me funciona porque el CLI hizo cosas" y entender cada pieza.


Manos a la obra: React + Vite + Tailwind

💻 Todos los ejemplos del post están listos para arrancar en Lemoncode/demos-post-lemoncode-tailwind. Este setup es la carpeta 01-hola-tailwind.

Paso 1: crear el proyecto

npm create vite@latest tailwind-sandbox -- --template react-ts
cd tailwind-sandbox
npm install

El scaffolding estándar de Vite con React + TypeScript. Nada nuevo bajo el sol.

Paso 2: instalar Tailwind y su plugin

npm install tailwindcss @tailwindcss/vite

Aquí van las dos primeras piezas. tailwindcss es el motor y @tailwindcss/vite es el adaptador que lo engancha a Vite.

Da igual si van como dependencia normal o de desarrollo: es tooling. Nada de esto viaja al navegador. Al navegador solo llega el CSS generado.

Paso 3: registrar el plugin en Vite

Abrimos vite.config.ts:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [react(), tailwindcss()],
});

Con esto Tailwind entra en el ciclo de vida de Vite:

  • En desarrollo, escanea tus ficheros y regenera el CSS cada vez que guardas.
  • En el build, genera el CSS final optimizado.

¿Recuerdas la lista content de la v3? Esto la sustituye. El plugin ya sabe qué ficheros son tuyos y cuáles ignorar (node_modules, binarios, lo que esté en .gitignore).

Paso 4: importar Tailwind en tu CSS

Abrimos src/index.css, borramos los estilos de demo de la plantilla y lo dejamos así:

@import "tailwindcss";

Una línea. Esta es la tercera pieza.

Cuando el compilador procese este fichero, ese import se expandirá en el CSS real: las variables del tema, el reset base y las utilidades que estés usando. Solo esas.

Comprueba que src/main.tsx importa este CSS con import "./index.css". En la plantilla de Vite ya viene así.

Paso 5: probar que funciona

Sustituimos src/App.tsx por nuestro botón:

export const App = () => {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <button className="bg-blue-600 hover:bg-blue-700 text-white text-lg font-semibold px-6 py-3 rounded-lg">
        Leer más
      </button>
    </div>
  );
};

export default App;

Y arrancamos:

npm run dev

El botón "Leer más" azul, centrado en pantalla: el setup mínimo funcionando

Si ves un botón azul centrado que se oscurece al pasar el ratón: enhorabuena, el circuito completo funciona.

Qué acaba de pasar por debajo

Esto es el corazón del invento:

  1. Vite ha arrancado.
  2. El plugin de Tailwind ha escaneado tu proyecto.
  3. Ha encontrado las clases que usas en App.tsx.
  4. Ha generado un CSS con exactamente esas reglas y ninguna más.
  5. Lo ha servido al navegador.

Pruébalo tú: añade un underline al botón y guarda. La regla aparece al instante. Bórrala, y desaparece del CSS.

Eso es el compilador trabajando en tiempo real.

Tres ficheros tocados. Cero ficheros de configuración de Tailwind.

Parte 4: Aprender a leer Tailwind

En resumen Las clases son la propiedad CSS abreviada más el valor. Los valores salen de escalas, no te los inventas. Los dos puntos son condiciones: hover:, md:, dark:.

Ya tenemos el botón en pantalla, pero lo hemos puesto un poco de fe. Releamos ese snippet, porque contiene media gramática de Tailwind.

<div className="flex min-h-screen items-center justify-center">
  <button className="bg-blue-600 hover:bg-blue-700 text-white text-lg font-semibold px-6 py-3 rounded-lg">
    Leer más
  </button>
</div>

El contenedor:

Clase Qué hace
flex activa flexbox (display: flex)
min-h-screen altura mínima igual al alto de la pantalla
items-center centra a los hijos en vertical
justify-center centra a los hijos en horizontal

Resultado: el botón clavado en el centro de la pantalla.

El botón:

Clase Qué hace
bg-blue-600 fondo azul, intensidad 600
hover:bg-blue-700 lo mismo un punto más oscuro, solo al pasar el ratón
text-white texto blanco
text-lg tamaño de texto grande
font-semibold peso seminegrita
px-6 py-3 padding horizontal y vertical
rounded-lg esquinas redondeadas

"¿Y esto cómo me lo aprendo de memoria?"

No te lo aprendes de memoria. Lo deduces. El sistema de nombres tiene una lógica muy consistente.

Regla 1: propiedad abreviada + valor

  • bg- → background
  • text- → lo relativo al texto
  • p- → padding, m- → margin
  • Con sufijos de dirección: px- horizontal, py- vertical, pt- top
  • w- → width, h- → height

Si sabes CSS, ya te sabes el 80% de Tailwind.

Y aquí una idea importante que conviene dejar por escrito: Tailwind no te ahorra saber CSS. Es una forma abreviada de escribirlo, no un sustituto de entenderlo.

Regla 2: los valores vienen de escalas

  • Espaciados: múltiplos de 0.25rem. p-4 es 1rem, px-6 es 1.5rem.
  • Colores: de 50 (casi blanco) a 950 (casi negro). blue-600 está un pelín más oscuro que el punto medio.
  • Tamaños discretos: tallas de camiseta. sm, md, lg, xl, 2xl.

Estas escalas son el sistema de restricciones. No eliges entre infinitos valores: eliges un peldaño de una escalera bien afinada.

Regla 3: los dos puntos son condiciones

Un prefijo con : significa "aplica esto solo cuando...".

  • hover: → cuando el ratón está encima
  • focus: → con el foco
  • md: → a partir de pantalla mediana (¡el responsive!)
  • dark: → en modo oscuro

Y se componen: md:hover:bg-blue-700 es "en pantalla mediana o mayor, al pasar el ratón".

Diagrama: la clase md:hover:bg-blue-700 diseccionada en condición + condición + propiedad + valor, y debajo las tres reglas — el diccionario de abreviaturas, las escalas de espaciado, color y tallas, y las condiciones

Tu mejor aliado: IntelliSense

Con estas tres reglas puedes leer la gran mayoría del Tailwind que te vas a encontrar.

Y para lo que no, instala la extensión oficial de VS Code: Tailwind CSS IntelliSense.

  • Autocompleta todas las clases, incluidas las que tú definas.
  • Te previsualiza una muestra del color junto a cada clase de la paleta.
  • Al pasar el ratón por una clase, te enseña el CSS exacto que genera.

Es la Piedra Rosetta perfecta mientras aprendes. Y también la forma rápida de auditar lo que tu asistente de IA te acaba de escribir.

Parte 5: Las capas

En resumen El CSS generado se organiza en cuatro capas: theme, base, components, utilities. Una capa posterior gana siempre a una anterior, sin importar la especificidad. Tu CSS sin capa flota por encima de todas. Ojo con eso.

Esta sección es la clave de todo lo que viene después. Si abres el CSS generado, verás que empieza así:

@layer theme, base, components, utilities;

Esa línea define el orden de prioridad, de menor a mayor.

Qué hay en cada capa

theme Las variables CSS de tu sistema de diseño: colores, espaciados, tipografías. Los famosos design tokens.

base El reset que elimina los estilos por defecto del navegador (se llama preflight), más los estilos base que tú des a elementos HTML a pelo: h1, a, body.

components Clases de componente. Y aquí un dato curioso: Tailwind la trae vacía.

No incluye ni un solo componente prediseñado. No hay ningún btn ni card de serie. Es el hueco reservado para lo que definas tú. Otra diferencia abismal con Bootstrap, que era precisamente todo componentes.

utilities Todas las utilidades: flex, bg-blue-600. La capa de mayor prioridad.

📦 Ese vacío de componentes lo cubre el ecosistema, con dos enfoques distintos:

daisyUI añade clases CSS semánticas (btn, card, alert) mediante el sistema de plugins de Tailwind.

shadcn/ui va por otro camino: copia componentes React en tu proyecto, construidos combinando utilidades. Sus botones o diálogos no son reglas dentro de una capa components. A shadcn/ui le dedicaremos su propio post.

La regla de oro

Una capa posterior gana a una anterior. Siempre. Sin importar la especificidad de los selectores.

Una utilidad simple de utilities machaca al selector más enrevesado de components.

Se acabó contar cuántas clases tiene cada selector para saber quién gana.

Si usas Photoshop o Figma, esto te suena

Es el panel de capas. Lo que está arriba tapa a lo que está abajo, dé igual lo elaborado que sea el contenido inferior.

Aquí igual: utilities está arriba del todo, así que tapa a components, que tapa a base.

Y un detalle extra que va a ser el protagonista del siguiente ejemplo: tu CSS escrito sin capa flota por encima de todas ellas. Como esa capa suelta que dejas arriba del todo del panel.

20260806114500-diagrama-capas.webp

El ejemplo clásico: la card que no obedece

Supón que creas una clase .card en tu index.css, así, a pelo:

@import "tailwindcss";

/* ⚠️ cuidado: esto está FUERA de las capas */
.card {
  background-color: white;
  border-radius: 0.5rem;
  padding: 1.5rem;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
}

Y la usas mezclándola con utilidades:

<div className="card">
  <h3 className="text-lg font-semibold">Tailwind: esto no es Bootstrap</h3>
  <p className="text-gray-600">Una guía para entender qué hay por debajo...</p>
</div>

Funciona de maravilla. Caja blanca, esquinas redondeadas, su sombrita.

Hasta que un día necesitas una card destacada con otro fondo:

<div className="card bg-amber-100">
  <h3 className="text-lg font-semibold">⭐ Post destacado</h3>
  <p className="text-gray-600">Este debería ir sobre fondo ámbar...</p>
</div>

Guardas, miras el navegador y... el fondo sigue siendo blanco.

20260806114548-02-card-sin-capa.webp

La clase bg-amber-100 está ahí. El CSS se ha generado (puedes verlo tachado en las dev tools). Pero no gana.

¿Por qué? Porque tu .card está fuera de las capas. Y el CSS sin capa flota por encima de todas, incluida utilities.

Es la capa suelta de Photoshop tapando tu edición.

La solución

Baja tu clase a la capa que le corresponde:

@import "tailwindcss";

@layer components {
  .card {
    background-color: white;
    border-radius: 0.5rem;
    padding: 1.5rem;
    box-shadow: 0 1px 3px rgb(0 0 0 / 0.1);
  }
}

El HTML no se toca. Exactamente el mismo <div className="card bg-amber-100">.

Pero ahora:

  • .card vive en components
  • bg-amber-100 vive en utilities, la capa de arriba

Y la card destacada aparece por fin con su fondo ámbar.

La misma card con la clase dentro de @layer components: el fondo ámbar de la utilidad por fin gana

La clase de componente pone los valores por defecto. Las utilidades los afinan caso a caso.

Este patrón de "base + ajuste" es de lo más potente de Tailwind. Y solo funciona si respetas las capas.

💻 Tienes las dos cards, la rota y la buena, en la demo 02-capas.

Parte 6: El problema del copia y pega

En resumen El problema real de Tailwind es la repetición de clases. Solución preferente en React: extraer un componente. Solución alternativa: una clase en components con @apply, pero con moderación.

Nuestro botón tiene ocho clases. Si mañana lo necesitamos en cinco pantallas, ¿qué hacemos?

¿Copiar y pegar el churro de clases cinco veces? ¿Y cuando diseño diga que el padding es muy generoso, lo cambiamos en cinco sitios? Con suerte, si los encontramos todos.

Este es el problema del enfoque utility-first, y no lo vamos a esconder: la repetición.

Tailwind tiene dos respuestas oficiales. Conviene conocer las dos, porque se usan en situaciones distintas.

Respuesta 1: extraer un componente (la preferente en React)

Estás en un framework de componentes. La abstracción ya la tienes gratis:

export const Button = ({ children, ...props }) => (
  <button
    className="bg-blue-600 hover:bg-blue-700 text-white text-lg font-semibold px-6 py-3 rounded-lg"
    {...props}
  >
    {children}
  </button>
);

El churro de clases vive en un único sitio. En el resto de la aplicación escribes <Button>Leer más</Button>.

¿Cambio de padding? Un fichero.

Esta es la vía que la documentación de Tailwind recomienda como primera opción con React, Vue o similares.

Y fíjate en el giro conceptual: la reutilización no se resuelve en el CSS, se resuelve en el sistema de componentes. Que para eso está.

Respuesta 2: una clase en la capa components

Hay casos donde un componente de React no llega:

  • HTML que no controlas: contenido de un CMS, markdown renderizado.
  • Primitivas muy pequeñas que quieres usar desde cualquier sitio.

Para eso está la capa que acabamos de conocer. Y puedes definir la clase reutilizando las propias utilidades, con la directiva @apply:

@layer components {
  .btn {
    @apply bg-blue-600 hover:bg-blue-700 text-white text-lg font-semibold px-6 py-3 rounded-lg;
  }
}

@apply hace lo que parece: coge esas utilidades y las inyecta como estilos de .btn.

Y ahora viene el pago por haber entendido las capas. Como .btn está en components y las utilidades están por encima, si un día necesitas ese botón en verde no hace falta crear nada nuevo:

<button class="btn">Leer más</button>
<button class="btn bg-green-600 hover:bg-green-700">Comprar</button>

La utilidad gana a la clase de componente por orden de capas. Así de simple.

El botón verde hereda todo lo demás (paddings, tipografía, bordes) y solo cambia lo que le has dicho.

20260806114849-03-botones.webp

Dos botones a partir de la misma clase .btn: el azul base y el verde afinado con dos utilidades

💻 Demo: 03-boton-reutilizable.

⚠️ Advertencia antes de que te vengas arriba

No conviertas esto en tu forma de trabajar por defecto.

Si acabas con .btn, .card, .title, .sidebar para todo, habrás reinventado el CSS de la era Bootstrap con pasos extra. Y habrás perdido justo lo que Tailwind aporta: todo a la vista, cambios locales, cero ficheros arqueológicos.

El propio equipo de Tailwind lleva años avisando del abuso de @apply.

La jerarquía sana:

  1. Utilidades en el marcado, por defecto.
  2. Componente de React, cuando hay repetición.
  3. Clase en components, solo para casos frontera.

Es la filosofía de siempre: la abstracción se crea cuando duele, no por si acaso.

Parte 7: Layout

En resumen Tailwind no tiene sistema de layout propio. Usa Flexbox y CSS Grid nativos. Cada clase es una propiedad CSS con nombre corto. Cero magia. Si no sabes flexbox y grid, Tailwind no te va a salvar.

Nos falta la pieza gorda: colocar cosas en pantalla. Y aquí hay otra diferencia conceptual importante con Bootstrap.

Bootstrap traía su propio sistema de layout. El grid de 12 columnas con container, row y col-md-8 era una abstracción inventada por el framework. Por debajo, en su época dorada, se cocinaba con floats y hacks de clearfix, que era lo que había.

Tú aprendías "el grid de Bootstrap", no CSS.

Tailwind no tiene sistema de layout propio. Ninguno.

Lo que te da son utilidades que mapean directamente a Flexbox y CSS Grid, los sistemas nativos del navegador.

Consecuencia: si sabes flexbox y grid, ya sabes hacer layout en Tailwind. Solo te falta el diccionario.

Y si NO los sabes, Tailwind no te va a salvar. Vas a copiar clases de un tutorial sin entender por qué funcionan, ni por qué dejan de funcionar.

El diccionario, sin magia

Ya lo has visto sin darte cuenta. Nuestro primer contenedor es flexbox puro:

Tailwind                 CSS de toda la vida
─────────────────        ─────────────────────────
flex                     display: flex;
items-center             align-items: center;
justify-center           justify-content: center;

Cada clase es una propiedad CSS con el nombre acortado.

Si dudas de qué hace items-center: hover en VS Code con IntelliSense y ves el CSS real.

Comparando con Bootstrap

¿Te acuerdas del snippet de Bootstrap que te pedí guardar en la retina? Contenido principal, barra lateral, 8 y 4 columnas.

Aquí lo tienes reconstruido con Tailwind y CSS Grid:

<div className="mx-auto max-w-5xl px-4">
  <div className="grid grid-cols-1 gap-6 md:grid-cols-3">
    {/* Contenido principal: 2 de 3 columnas */}
    <main className="md:col-span-2">
      <h2 className="text-2xl font-bold">Mi artículo</h2>
      <p className="text-gray-600">Un texto interesantísimo...</p>
      <button className="btn">Leer más</button>
    </main>
    {/* Barra lateral: 1 de 3 columnas */}
    <aside className="card">
      <h3 className="font-semibold">Relacionados</h3>
      <p className="text-gray-600">Más contenido...</p>
    </aside>
  </div>
</div>

De paso hemos reutilizado nuestro .btn y nuestra .card. Todo va encajando.

Leámoslo con las gafas que ya tenemos:

mx-auto max-w-5xl px-4 El equivalente al container de Bootstrap, pero desmontado en sus piezas: ancho máximo, centrado horizontal y un poco de aire a los lados. Tres decisiones que Bootstrap tomaba por ti, aquí a la vista.

grid grid-cols-1 gap-6 Activa CSS Grid, con una sola columna y separación entre celdas. Esto es lo que aplica en móvil.

md:grid-cols-3 A partir de pantalla mediana, la rejilla pasa a tres columnas. Ahí está el patrón de variantes haciendo de media query.

md:col-span-2 El contenido principal ocupa dos de las tres columnas. La barra lateral se queda con la que sobra.

El layout en escritorio: contenido principal ocupando dos columnas y barra lateral en la tercera

El layout en móvil: los mismos bloques, apilados en una sola columna

💻 Demo: 04-layout.

Es exactamente el comportamiento del col-md-8 / col-md-4 de Bootstrap. Apilado en móvil, dos tercios y un tercio en pantalla grande. Mobile first.

Solo que ahora sin trucos del framework:

  • grid-cols-3 es literalmente display: grid; grid-template-columns: repeat(3, minmax(0, 1fr))
  • md: es una media query estándar

El navegador hace todo el trabajo. Tailwind solo le pone nombre corto a las piezas.

💡 La idea con la que quedarte

Cuando tu asistente te genere un layout y algo no cuadre (que pasará), el problema no va a ser "de Tailwind". Va a ser de flexbox o de grid.

Y se depura con conocimiento de flexbox y de grid:

  • Por qué un hijo no se estira.
  • Por qué una columna se desborda.
  • Por qué el min-width por defecto de un item flex te rompe el truncado de texto.

Tailwind es la notación. El layout es el de siempre.

Invertir en entender bien flexbox y grid sigue siendo de lo más rentable que puedes hacer como desarrollador front. Uses Tailwind o no.

Parte 8: Las variantes

En resumen Las variantes son los prefijos con dos puntos: hover:, md:, dark:, group-hover:. Cubren estados, responsive, modo oscuro y relaciones entre elementos. Se componen entre sí y son imposibles de hacer con estilos inline.

Llevamos medio post usando hover: y md: prometiendo hablar de esto más adelante. Pues ya estamos en "más adelante".

Merece la pena, porque las variantes son posiblemente donde Tailwind más brilla. Y donde más se nota en el código que genera una IA.

El patrón ya lo conoces: condición:utilidad.

Vamos con el mapa completo, por familias.

Familia 1: estados interactivos

hover:, focus:, active:, disabled:

Los pseudo-estados de CSS de toda la vida, aplicables a cualquier utilidad.

Nuestro botón, en versión de producción:

<button
  class="btn focus:ring-2 focus:ring-blue-300 active:bg-blue-800
         disabled:opacity-50 disabled:cursor-not-allowed"
>
  Enviar
</button>

Léelo con calma:

  • Un anillo de foco para accesibilidad al navegar con teclado.
  • Un tono más oscuro mientras está pulsado.
  • Semitransparente y con cursor de prohibido cuando está deshabilitado.

Cuatro estados del botón, descritos en el propio botón, sin salir del HTML.

Familia 2: responsive

Tailwind trae cinco breakpoints de serie:

Prefijo A partir de
sm: 640px
md: 768px
lg: 1024px
xl: 1280px
2xl: 1536px

Y ahora la regla que tienes que grabarte a fuego, porque es la fuente número uno de confusión de los recién llegados:

Una clase sin prefijo aplica a TODOS los tamaños. El prefijo significa "a partir de".

Es decir: md: no es "en pantalla mediana". Es "de mediana para arriba".

Se piensa el móvil primero y se va corrigiendo hacia arriba. El mobile first de siempre.

<h2 class="text-xl md:text-3xl 2xl:text-5xl">Titular que escala</h2>
  • Móvil: tamaño xl
  • De tablet para arriba: 3xl
  • En monitores enormes: 5xl

Dos media queries expresadas en una línea, sin escribir ninguna.

Familia 3: modo oscuro

La variante dark: aplica sus utilidades cuando el usuario está en modo oscuro. Por defecto sigue la preferencia del sistema operativo, con la media query prefers-color-scheme.

<div class="card bg-white text-gray-900 dark:bg-gray-800 dark:text-gray-100">
  Me adapto a tu tema sin JavaScript
</div>

Piensa lo que costaba esto antes: duplicar hojas de estilos, toggles con clases en el body, cascadas paralelas.

Y lo que cuesta aquí: prefijar las utilidades que cambian.

El modo oscuro pasa de "épica de dos sprints" a decisión local de cada componente.

La demo de variantes en modo claro: botones con estados, titular responsive y cards

La misma demo en modo oscuro: fondos y textos adaptados solo con la variante dark:

Familia 4: variantes relacionales

Estas son las que más despistan la primera vez que las ves. Y las que más molan cuando las entiendes.

A veces la condición no está en el propio elemento, sino en otro.

Ejemplo: quiero que al pasar el ratón por la card, el título cambie de color. El hover es de la card, el estilo es del título.

Para eso está la pareja group / group-hover::

<div class="card group">
  <h3 class="font-semibold group-hover:text-blue-600">Post destacado</h3>
  <p class="text-gray-600">Pasa el ratón por cualquier parte de la card...</p>
</div>

Marcas al padre como group. En el hijo usas group-hover:, que significa "cuando el ratón esté sobre mi grupo, aplícame esto".

La card con el ratón encima: el título se pinta de azul gracias a group-hover

Existe también la variante hermana peer / peer-*, que hace lo mismo entre elementos vecinos. El caso clásico: mostrar el mensaje de error cuando el input de al lado es inválido, con peer-invalid:visible.

Sin JavaScript. Sin estados de React para un simple efecto visual.

💻 Demo: 05-variantes.

Y todo se compone

Las variantes se encadenan:

dark:md:hover:bg-blue-800 → "en modo oscuro, en pantalla mediana o mayor, al pasar el ratón".

Cada combinación genera exactamente la regla CSS que escribirías a mano, media query incluida. Pero la escribes en tres prefijos.

Aquí queda claro de una vez que esto no tiene nada que ver con estilos inline. Esa cuenta la saldaremos al final del post.

La válvula de escape: valores arbitrarios

Hay una sintaxis más que te vas a encontrar sí o sí en código generado por IA: los corchetes.

<div class="w-[137px] bg-[#1a2b3c]">...</div>

Son los valores arbitrarios. Cuando ningún peldaño de la escala te vale (ese banner que por diseño mide exactamente 137px), metes el valor a pelo entre corchetes y Tailwind genera la utilidad al vuelo.

Es la válvula de escape oficial. Usada con cuentagotas, perfectamente legítima.

🚨 Pero fíjate en la señal de alarma

Esta es de las cosas más útiles que te llevas del post.

Si el código que te devuelve tu asistente viene plagado de corchetes, te está diciendo a gritos que está improvisando valores en lugar de usar tu sistema de diseño.

  • bg-[#3b82f6] en vez de bg-blue-500
  • p-[16px] en vez de p-4

Cada corchete de esos es un valor huérfano. Quedará descolgado el día que cambies un color o un espaciado del proyecto.

¿La corrección? Obligarte a usar las escalas del proyecto. La herramienta para definirlas y enseñárselas es justo la siguiente sección: el tema.

De momento, quédate con el truco: un vistazo rápido buscando corchetes es el code review más barato que existe.


Bonus: que las clases se ordenen solas

Con tanta clase junta, el orden importa para leer.

No es lo mismo encontrarte las utilidades siempre en el mismo orden (layout, espaciado, tipografía, color, estados) que en el orden aleatorio que se le ocurrió a cada uno. O a cada IA.

Para eso existe prettier-plugin-tailwindcss, el plugin oficial de Tailwind para Prettier. Reordena las clases automáticamente al guardar, siguiendo el orden canónico de la documentación.

Dos beneficios:

  • Cualquiera del equipo, humano o máquina, encuentra cada clase donde espera.
  • Los diffs de las pull requests dejan de ensuciarse con reordenaciones cosméticas.

Instálalo desde el primer día.

Parte 9: El tema

En resumen Las utilidades se derivan de tokens: cambia el token y se regenera todo. Defines tus tokens en CSS, con el bloque @theme. Ese fichero es el contrato de diseño de tu proyecto, y cabe en una pantalla.

Hasta ahora hemos vivido de las escalas que Tailwind trae de serie. Pero en un proyecto real, la primera petición que llega es de manual: "usa los colores corporativos".

Aquí brilla el diseño de la v4, porque el mecanismo es elegantísimo:

Las utilidades no están escritas a mano. Se derivan de los tokens del tema.

bg-blue-600 existe porque en la capa theme hay una variable --color-blue-600.

Cambia los tokens, y toda la maquinaria se regenera sola.

Definir tokens propios

¿Dónde? En tu CSS, con el bloque @theme.

@import "tailwindcss";

@theme {
  --color-brand-500: #8b5cf6;
  --color-brand-600: #7c3aed;
  --color-brand-700: #6d28d9;
}

Tres variables. Y ahora mira todo lo que acaba de aparecer sin declarar nada más:

Se propaga a todas las familias de utilidades que aceptan un color: bg-brand-600, text-brand-500, border-brand-700, ring-brand-500, shadow-brand-600...

Y cada una funciona con todas las variantes: hover:bg-brand-700, dark:text-brand-500, md:border-brand-600...

Diagrama: un token --color-brand-600 definido en @theme se multiplica por las familias de utilidades (bg-, text-, border-, ring-, shadow-) y por todas las variantes, dando cientos de clases; de bonus, un token --breakpoint-3xl crea la variante 3xl: entera

Tres líneas de CSS y tienes literalmente cientos de combinaciones. Con autocompletado incluido, porque IntelliSense lee tu @theme y te ofrece brand con su muestrita de color, como si fuera de la casa.

Nuestro botón corporativo ya no necesita el azul de serie:

<button class="btn bg-brand-600 hover:bg-brand-700">Leer más</button>

⚠️ Un matiz: las tonalidades no se inventan solas. Si solo defines --color-brand-500, no existe brand-300. Los peldaños los pones tú.

Lo que es gratis no son los tonos, sino la multiplicación: token × familias × variantes.

Esto va mucho más allá de los colores

@theme {
  --font-display: "Poppins", sans-serif;
  --breakpoint-3xl: 120rem;
}

La primera línea hace aparecer la utilidad font-display, tipografía para titulares.

La segunda es aún más llamativa: crea el breakpoint 3xl, sumándose a los cinco de serie. Y con él, la variante 3xl: entera.

A partir de ese momento puedes escribir 3xl:grid-cols-4 o 3xl:text-2xl en cualquier sitio.

Piénsalo un segundo: has creado una media query nueva para todo tu proyecto declarando una variable CSS.

En la era Bootstrap esto era recompilar el framework.

Bonus: tus tokens son CSS de verdad

Estas variables no son un artefacto de compilación. Acaban en el CSS final como variables CSS reales, en la capa theme.

O sea que puedes leerlas desde cualquier sitio:

  • var(--color-brand-600) en un CSS tuyo.
  • Desde JavaScript con getComputedStyle, por ejemplo para pasarle el color corporativo a una librería de gráficas.

Tu tema no vive encerrado en Tailwind. Es CSS estándar consultable por cualquiera.

¿Y si quiero crear una utilidad nueva?

También hay vía oficial: la directiva @utility.

Ejemplo clásico, ocultar la barra de scroll (no hay utilidad de serie para eso):

@utility scrollbar-hidden {
  scrollbar-width: none;
  &::-webkit-scrollbar {
    display: none;
  }
}

¿Por qué @utility en vez de una clase normal?

Porque al registrarla con la directiva, Tailwind la trata como ciudadana de primera. Entra en el sistema de variantes, y automáticamente funcionan md:scrollbar-hidden, hover:scrollbar-hidden o cualquier combinación.

Otra vez la multiplicación gratis, ahora con una utilidad tuya. Una clase suelta escrita a mano jamás se enteraría de que existe md:.

💡 El punto clave de esta sección

Este fichero CSS (tu @theme, tus @utility, tus clases en components) es el contrato de diseño de tu proyecto. Y cabe en una pantalla.

Cuando trabajes con un asistente de IA, ese fichero es oro puro. Pásaselo como contexto y pídele explícitamente que use tus tokens.

La diferencia entre una IA que te genera pantallas con veinte azules distintos y una que respeta tu sistema de diseño suele estar, exactamente, en si le has enseñado este fichero.

Parte 10: Guía práctica del tema en 4 pasos

En resumen Necesitas menos entradas de las que crees: primario, secundario, un neutro, semánticos, tipografías y radio. Nombra por rol, no por color. La IA es buenísima generando escalas si le das el prompt correcto.

Me pongo con una aplicación nueva. ¿Qué meto en el @theme?

Guía de mínimos que cubre el 90% de los casos.

Paso 1: decide qué entradas necesitas

primary El color de marca, el de las acciones principales: botones, enlaces, elementos activos. Con su escala de tonalidades, porque lo usarás en fondos claros (primary-50), en color pleno (primary-600) y en estados oscuros (primary-700).

secondary (o accent) El color de apoyo para acentos y elementos secundarios.

Un neutro La familia de grises que dominará la aplicación: textos, fondos, bordes.

Aquí el truco es no inventar. Elige una de las paletas de serie y dale nombre semántico:

  • slate tira a azulado
  • zinc es neutro puro
  • stone es cálido

Los semánticos success, warning, danger. También suelen resolverse por alias a paletas de serie.

Las tipografías La de texto y, si la hay, la de titulares.

El radio de esquinas Parece un detalle menor, pero define muchísimo la personalidad. Esquinas vivas = serio y corporativo. Muy redondeado = amable y juguetón.

💎 Un consejo que vale oro

Nombra por rol, no por color.

primary-600, no violet-600.

El día que marketing decida que la marca ahora es verde azulado, cambias los valores del tema y no tocas ni una línea de componente.

Paso 2: el tema a mano

Versión compacta. En tu proyecto, la escala de primary tendría sus once peldaños, de 50 a 950.

@import "tailwindcss";

@theme {
  /* Marca */
  --color-primary-50: #f5f3ff;
  --color-primary-100: #ede9fe;
  --color-primary-500: #8b5cf6;
  --color-primary-600: #7c3aed;
  --color-primary-700: #6d28d9;
  --color-primary-950: #2e1065;

  /* Secundario: alias de una paleta de serie */
  --color-secondary-500: var(--color-teal-500);
  --color-secondary-600: var(--color-teal-600);
  --color-secondary-700: var(--color-teal-700);

  /* Semánticos */
  --color-success-600: var(--color-green-600);
  --color-warning-600: var(--color-amber-600);
  --color-danger-600: var(--color-red-600);

  /* Tipografía y personalidad */
  --font-sans: "Inter", sans-serif;
  --font-display: "Poppins", sans-serif;
  --radius-lg: 0.75rem;
}

Fíjate en la mezcla de estrategias:

  • La escala de primary con valores propios, porque es TU marca.
  • El resto por alias a las paletas de serie, que están muy bien pensadas y no hay por qué reinventarlas.

A partir de aquí, en toda la aplicación se escribe bg-primary-600, text-danger-600, font-display. Y el tema es la única fuente de verdad.

La escala primary de 50 a 950 y botones primario, secundario y peligro generados desde el tema

💻 Demo: 06-tema.

Paso 3: que te lo genere la IA

Confesémoslo: interpolar once tonalidades armoniosas a partir de un color de marca es orfebrería cromática que ningún desarrollador quiere hacer a mano.

Es el caso ideal para tu asistente. Si le das las instrucciones precisas.

Un prompt que funciona bien:

El color de marca de mi aplicación es #7c3aed y el secundario #0d9488. Genérame un tema para Tailwind CSS v4 usando el bloque @theme con variables CSS (formato --color-primary-*), NO el fichero tailwind.config.js de la v3.

Quiero: escala completa de 50 a 950 para primary y secondary, colores semánticos success/warning/danger como alias de las paletas de serie, y que el texto blanco sobre primary-600 cumpla contraste WCAG AA.

Cada trozo de ese prompt está ahí por algo que ya hemos visto:

  • Se especifica v4 y el formato @theme porque, si no, la cabra tira al monte y te devuelve un tailwind.config.js de la era v3.
  • Se piden los alias para no acabar con cincuenta hexadecimales imposibles de mantener.
  • Se exige contraste AA porque es el error silencioso más común de las escalas generadas: tonos medios preciosos sobre los que el texto blanco no se lee.

Y como a estas alturas ya sabes leer el resultado, puedes auditarlo en dos minutos en vez de pegarlo a ciegas. Que era el objetivo declarado de este post.

Paso 4: inyectarlo

Aquí no hay misterio, y esa es la gracia.

El bloque @theme que te ha devuelto la IA se pega en tu index.css, debajo del @import "tailwindcss".

Guardas, y la cadena entera reacciona sola:

  • El compilador regenera las utilidades.
  • IntelliSense empieza a autocompletar primary-600 con su muestra de color.
  • El hot reload te lo pinta en pantalla.

Si el tema crece y prefieres tenerlo aparte, extráelo a un src/theme.css e impórtalo desde el principal con @import "./theme.css";. Para eso es CSS estándar.

Consejo extra: tu identidad corporativa no se apellida Tailwind

Hay una vuelta de tuerca más que en proyectos serios marca la diferencia.

Los colores de marca, las tipografías y demás señas de identidad de tu empresa no deberían tener como fuente de verdad un fichero en formato Tailwind.

Sácalos a variables CSS a pelo, en su propio fichero, sin dependencia de framework:

/* tokens.css — identidad corporativa, CSS estándar, cero Tailwind */
:root {
  --acme-brand-600: #7c3aed;
  --acme-brand-700: #6d28d9;
  --acme-font-title: "Poppins", sans-serif;
}

Y que sea el @theme de Tailwind quien los consuma por alias:

@import "tailwindcss";
@import "./tokens.css";

@theme {
  --color-primary-600: var(--acme-brand-600);
  --color-primary-700: var(--acme-brand-700);
  --font-display: var(--acme-font-title);
}

¿Qué ganas con esta capa extra de indirección?

Que tu identidad visual deja de estar casada con Tailwind.

El día que el proyecto migre a otro framework CSS, o que convivan varios, los tokens siguen siendo válidos tal cual. Son CSS estándar que cualquier herramienta puede leer.

Así es como funcionan las empresas grandes: los design tokens viven en una librería propia, versionada y publicada aparte. Cada aplicación se los trae como una dependencia más.

Da igual si esa aplicación usa Tailwind, MUI, styled-components o CSS a pelo. Todas beben de la misma fuente y la marca se ve igual en todas partes.

No es un escenario teórico. La convivencia Tailwind + Material UI es un combo muy resultón: Tailwind para layout y estilos finos, MUI para componentes ricos como tablas o date pickers, ambos leyendo los mismos tokens y en paz gracias a las capas nativas.

Tiene su propio post en este blog: Tailwind CSS + Material UI: lo mejor de ambos mundos.

Parte 11: Cerrando el círculo

Hemos hecho el viaje completo. De la era Bootstrap al compilador, de las capas al tema, de las variantes a la guía práctica.

Aún dos quedan algunas cuentas por saldar. Este es el momento de pagarlas.

No, esto no es CSS inline con pasos extra

Es la crítica más repetida desde 2017. A primera vista parece razonable: si al final escribo los estilos en el propio elemento, ¿en qué se diferencia de un style="..."?

Se diferencia en todo. Vamos por partes.

1. Los estilos inline no tienen variantes. Un style= no puede expresar hover:, ni md:, ni dark:, ni group-hover:. No hay forma humana de meter una pseudo-clase o una media query en el atributo style. Es una limitación del lenguaje.

Este argumento por sí solo ya cierra el debate. Pero sigamos.

2. Los estilos inline no tienen sistema. style="padding: 17px" es un valor huérfano sacado de la nada. p-4 es un peldaño de una escala compartida y gobernada desde el tema.

Uno multiplica la inconsistencia. El otro la previene.

3. Los estilos inline no tienen capas. Un style= machaca a casi todo por especificidad y no hay quien lo sobrescriba limpiamente.

Una utilidad vive en la capa utilities, con su lugar bien definido en la cascada: por encima de tus clases de componente, por debajo de tu CSS sin capa. Juega en el sistema, no contra él.

4. Los estilos inline no compilan a nada. Mil botones con style= son mil copias de los mismos estilos engordando el HTML. Mil botones con p-4 comparten una regla CSS en un fichero minificado y cacheado.

En resumen: Tailwind se parece a los estilos inline en dónde se escribe. Se diferencia en todo lo que importa.

Esta polémica ya la vivimos: JSX

Si te suena de algo, es porque es calcada.

Cuando React apareció en 2013 mezclando marcado dentro de JavaScript, medio mundo se echó las manos a la cabeza. "¡La separación de responsabilidades es sagrada!". "¡Esto es volver al spaghetti de PHP!".

Una década después, JSX es el estándar de facto. Lo usan también Solid, Preact y compañía.

Y hasta los frameworks que no lo adoptaron, como Svelte o Vue, abrazaron la misma idea de fondo con sus componentes de fichero único: plantilla, lógica y estilos, todo junto.

La lección de aquella batalla es exactamente la que Adam Wathan aplicó al CSS:

La separación útil no es por tecnología, sino por componente. Lo que cambia junto, vive junto.

A JSX le costó unos años pasar de herejía a ortodoxia. Tailwind ha recorrido el mismo camino, con la misma indignación inicial incluida.

Pros y contras

✅ Lo que Tailwind hace muy bien

Velocidad y cambios sin miedo. Los estilos son locales al marcado. El radio de destrucción de cada cambio es mínimo. Y te ahorras el eterno "¿cómo llamo a esta clase?".

Consistencia por diseño. Las escalas y el tema hacen que lo fácil sea lo correcto. Salirse del sistema requiere esfuerzo consciente, y los corchetes se ven venir.

CSS final mínimo y estable. Solo genera lo que usas. Y como las clases se reutilizan, el CSS crece mucho más despacio que la aplicación.

Sin "olor a Tailwind". El diseño es tuyo. El framework no impone estética.

Un solo fichero de contrato. Tema, utilidades propias y componentes CSS en un sitio consultable por humanos e IAs.

Es el estándar de facto de la era IA. Los asistentes lo hablan con fluidez nativa, y todo el tooling rema a favor.

❌ Lo que hay que pagar a cambio

El HTML se vuelve denso. Con oficio y el plugin de Prettier se lee rápido, pero las primeras semanas el churro de clases impone. Es un coste real. Se amortiza, pero existe.

No te ahorra saber CSS. Si tu equipo no domina flexbox, grid y la cascada, Tailwind no tapa ese agujero. Lo decora.

La repetición exige disciplina. Sin la jerarquía sana que vimos, un proyecto Tailwind degenera en copia-pega inconsistente. El framework no te obliga a hacerlo bien.

Necesita build. No hay CDN-y-a-correr. Es un compilador en tu pipeline. En 2026 casi todo proyecto ya tiene build, pero es una dependencia más que mantener.

El diseño no viene incluido. Bootstrap te regalaba un criterio estético decente. Tailwind te da libertad total, y la libertad total en manos de quien no tiene criterio (humano o IA sin guiar) produce interfaces incoherentes. Los raíles evitan que te salgas de la escala, no que combines mal los colores.

Hay mucho material desactualizado. Una montaña de tutoriales y código v3 circulando, también en el entrenamiento de tu asistente. Ya sabes detectarlo, pero es fricción presente.


¿Y cuándo NO te conviene?

Yo me pensaría dos veces meter Tailwind si:

Ya tienes una librería de componentes que lo cubre todo. Si tu aplicación es un back-office montado enteramente sobre MUI o Ant Design, con su tema bien configurado, Tailwind aporta poco más que otra forma de escribir lo mismo.

(Otra cosa es el escenario híbrido: la librería para componentes ricos, Tailwind para layout y ajustes.)

Tienes un proyecto legacy con miles de líneas de CSS que funcionan. Meter Tailwind ahí no es una migración, es una convivencia larga entre dos mundos, con su coste. Puede compensar en secciones nuevas. Rehacer lo que ya funciona, rara vez.

Tu equipo espera que Tailwind le ahorre aprender CSS. Ya sabes cómo acaba eso. Primero flexbox y grid. Tailwind después.

El proyecto es minúsculo o no tiene build. Para una landing estática de tres pantallas, un fichero CSS de cien líneas con CSS moderno es una solución perfectamente digna.

Tu empresa ya tiene un design system consolidado en otra tecnología. Ahí la conversación no es "Tailwind sí o no". Es la de los tokens compartidos que vimos antes. Quizá Tailwind entre, pero subordinado a ese sistema.

Tu web es puro diseño: dirección de arte, animación, scroll fino. Piensa en campañas, portfolios, sites de premio tipo Awwwards. Animaciones complejas, scroll coreografiado, cada pieza a medida.

Ese trabajo vive en @keyframes elaborados, clip-path, custom properties animadas, integración con GSAP. Cosas que las utilidades no expresan y que acaban igualmente en ficheros CSS.

Y hay un porqué humano: un diseñador que domina CSS piensa directamente en CSS. Para él, el diccionario de utilidades es fricción. Y el sistema de restricciones que es oro en una app de producto aquí es un corsé, porque la gracia está justo en salirse de la escala.

No es casualidad que los críticos más sólidos de Tailwind salgan del gremio del CSS artesano. Para su caso de uso, tienen razón.

El patrón

Tailwind brilla en interfaz de producto: aplicaciones, dashboards, SaaS. Ese frontend sistemático donde la consistencia vale más que el alarde visual.

Encaja especialmente bien en proyectos con proceso de build y equipos que dominan CSS. También cuando utilizas una IA, siempre que esté bien guiada por alguien que entienda lo que está generando.

Cuanto más te alejes de este escenario, menos ventajas ofrece Tailwind.

El huevo y la castaña

Cerramos la metáfora que abrimos al principio.

¿Por qué comparar Tailwind con Bootstrap era comparar un huevo con una castaña? Porque aunque los dos llevan la etiqueta "framework CSS", son productos de especies distintas.

Bootstrap era un producto de diseño terminado. Componentes hechos, decisiones estéticas tomadas, grid propio. Tú ensamblabas. Rápido, uniforme... y con olor.

Tailwind es una herramienta para fabricar TU diseño. No trae ni un botón, no opina de estética. A cambio te da un compilador, un sistema de restricciones y un lenguaje para expresar cualquier diseño de forma consistente. Tú decides.

No compiten en la misma categoría. Bootstrap competía por vestirte la aplicación. Tailwind compite, en realidad, con escribir CSS a mano.

Por eso la pregunta "¿Tailwind o Bootstrap?" estaba mal planteada desde el principio. La pregunta real es: ¿quiero un diseño prestado o herramientas para construir el mío?

En 2011, con aquellos navegadores y aquellas prisas, el diseño prestado era un chollo.

En 2026, con CSS moderno, componentes y asistentes de IA generando interfaz a destajo, el valor está en el sistema propio: tokens tuyos, criterio tuyo, y una notación que humanos y máquinas leen sin ambigüedad.

El mensaje final

Tu asistente de IA va a seguir generando Tailwind, se lo pidas o no. La cabra seguirá tirando al monte.

La diferencia está en el piloto. Ahora sabes:

  • Leer el churro de clases y detectar el que sobra.
  • Distinguir un setup v4 de uno v3 rancio.
  • Detectar los corchetes que ignoran tu sistema de diseño.
  • Entender por qué un estilo no gana (capas, siempre las capas).
  • Y sobre todo, entregarle a la máquina el fichero de tema que convierte sus improvisaciones en TU diseño.

La IA escribe rápido. El criterio sigue siendo cosa tuya.

Y eso, ni Bootstrap ni Tailwind ni el modelo de moda te lo van a dar hecho.


💻 Todo el código de los ejemplos, listo para clonar y trastear: Lemoncode/demos-post-lemoncode-tailwind.


¿Necesitas ayuda con tu proyecto?

En Lemoncode llevamos un montón de proyectos a nuestras espaldas construyendo aplicaciones de gestión. Con Tailwind, con shadcn/ui, con Material UI, y con varios de ellos conviviendo en paz.

Los desafíos que van más allá de este post ya los tenemos resueltos y rodados:

  • Sistemas de diseño y tokens corporativos
  • Autenticación y autorización
  • Gestión de formularios
  • Datos globales y caché
  • Cómo encajar las herramientas de IA en el flujo del equipo sin perder el criterio

Si te toca arrancar (o rescatar) una aplicación de este tipo y quieres ir sobre seguro, ofrecemos servicios de consultoría y desarrollo.

Escríbenos a info@lemoncode.net y cuéntanos tu caso. Estaremos encantados de escucharte.