Módulo 03: Espaciado y tamaño

Objetivos del módulo

  • Relacionar cada capa del modelo de caja con la familia de clases que la controla
  • Entender por qué w-* incluye el relleno y el borde, y cuándo usar box-content
  • Usar la escala de espaciado con soltura y saber qué valores existen en cada versión
  • Aplicar relleno y margen a un lado, a un eje o a todos, sin dudar del sufijo
  • Centrar con mx-auto, empujar con ms-auto y solapar con márgenes negativos
  • Elegir entre gap-*, space-* y márgenes, y explicar por qué gap-* suele ser la mejor opción
  • Controlar la anchura con la escala, fracciones, nombres y palabras clave (w-fit, w-max…)
  • Saber por qué h-full a veces no hace nada
  • Distinguir vh, svh, lvh y dvh, y usarlos en móviles
  • Usar size-*, min-* y max-*, y resolver el clásico min-w-0 de un texto que desborda
  • Escribir interfaces que funcionen de izquierda a derecha y de derecha a izquierda con propiedades lógicas
  • Maquetar una página completa de la tienda solo con lo aprendido
  • Traducir cualquier clase de este módulo a Tailwind 3.4 si eliges esa versión

1. Preparación del laboratorio

Seguimos con el proyecto Vite del módulo 01. Basta con un app.css con @import "tailwindcss"; y una página como esta:

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Espaciado</title>
  <link rel="stylesheet" href="/app.css">
</head>
<body class="bg-white text-gray-900">
  <!-- Aquí van los ejemplos -->
</body>
</html>

Arranca npm run dev y deja el navegador abierto. Recuerda el aviso del módulo 02: si creas un HTML con el servidor en marcha y una clase no aparece, guarda app.css para forzar un nuevo escaneo.

Algunos ejemplos colocan elementos con clases de otros módulos. Te basta con esta idea por ahora:

Clases Qué hacen Dónde se estudian
flex, grid, grid-cols-3, items-center Colocan a los hijos en fila o en rejilla Flexbox y Grid
flex-1, shrink-0 Reparten o protegen el espacio de un hijo flex Flexbox
text-sm, font-bold, truncate Texto Tipografía
bg-indigo-200, text-gray-600 Color Color y fondos
border, rounded-lg, shadow-sm Bordes y sombras Bordes, sombras y efectos
sm:, lg: Aplican una clase a partir de un ancho Diseño responsive

Si quieres repasar el modelo de caja desde cero, tienes el Curso de HTML y CSS.


2. El modelo de caja en clases de Tailwind

Todo elemento se dibuja como una caja de cuatro capas: contenido, relleno (padding), borde y margen. Casi todo este módulo consiste en poner valor a esas capas.

Modelo de caja con margen, borde, relleno y contenido, y las familias de clases que controlan cada capa

Figura 1. Cada capa de la caja tiene su familia de clases. La anchura w-* llega hasta el borde porque Preflight activa box-border.

Capa Clases Propiedades CSS Se pinta con el fondo
Relleno p-* y variantes padding Sí
Borde border-* border Tiene su propio color
Margen m-* y variantes margin No, es transparente
Tamaño w-*, h-*, size-*, min-*, max-* width, height…  
Entre hijos gap-*, space-* gap, o márgenes en los hijos No

La diferencia práctica entre relleno y margen es sencilla: el relleno agranda la zona clicable y coloreada de un botón; el margen separa ese botón de lo que tiene alrededor. Si dudas, pregúntate si quieres que el fondo llegue hasta ahí.

box-border y box-content

Por defecto, CSS mide width solo hasta el contenido: el relleno y el borde se suman por fuera. Es incómodo, porque una caja de 100% con relleno se sale de su padre. Por eso Preflight aplica a todos los elementos box-sizing: border-box, como viste en el módulo 02. Las dos clases de esta familia cambian ese valor:

<div class="box-border w-48 border-8 border-indigo-600 bg-indigo-100 p-6">
  <p class="font-mono text-sm">box-border</p>
</div>
<div class="box-content w-48 border-8 border-orange-500 bg-orange-100 p-6">
  <p class="font-mono text-sm">box-content</p>
</div>
.box-border {
  box-sizing: border-box;
}
.box-content {
  box-sizing: content-box;
}

Dos cajas con las mismas clases de tamaño: la de box-content es más ancha

Figura 2. Las dos llevan w-48 (192 px). La primera mide 192 px en total; la segunda, 256 px.

Medidas con getBoundingClientRect() en el navegador: box-border da 192 px, y box-content da 192 + 2 × 24 de relleno + 2 × 8 de borde = 256 px.

Con Preflight activo, box-border no cambia nada: ya es el valor de todos los elementos. Te hará falta box-content en contadas ocasiones, por ejemplo cuando quieres que el contenido mida exactamente una cantidad y el relleno se añada alrededor. También es útil box-border si has desactivado Preflight.

💡 Consejo: Abre las herramientas del navegador, selecciona un elemento y mira el diagrama de la pestaña Computed. Muestra margen, borde, relleno y contenido con sus medidas reales. Es la forma más rápida de saber de dónde sale un espacio.


3. La escala de espaciado en la práctica

En el módulo 02 viste de dónde sale la escala: una variable --spacing de 0.25rem multiplicada por el número de la clase. Aquí nos interesa usarla.

Clase px Uso habitual
0.5, 1 2, 4 Separación entre icono y texto
2, 3 8, 12 Relleno de botones y etiquetas
4, 6 16, 24 Relleno de tarjetas, separación de bloques
8, 12 32, 48 Separación entre secciones
16, 24 64, 96 Márgenes grandes de portada
32 a 96 128 a 384 Anchos y altos de elementos

La misma escala sirve para p-, m-, gap-, space-, w-, h-, size-, min- y max-. Por eso w-4 y p-4 miden lo mismo.

📘 Concepto: Trabajar con una escala limita las opciones a propósito. Si todos los espacios salen de la misma serie, la interfaz parece ordenada sin que nadie lo decida en cada caso, y se acaban los debates entre 13px y 14px.

Multiplicadores libres 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) solo existen los valores de una lista cerrada, y p-13 o w-150 no generan CSS. Si usas la LTS: usa un valor arbitrario como p-[3.25rem], o añade el valor a theme.extend.spacing en tailwind.config.js.

En 4.x el número es un multiplicador libre, siempre que sea múltiplo de 0.25. Este es el resultado de compilar cada clase en las dos versiones:

Clase Tailwind 3.4.19 Tailwind 4.3.3
p-13 No genera CSS calc(var(--spacing) * 13)
p-1.25 No genera CSS calc(var(--spacing) * 1.25)
p-1000 No genera CSS calc(var(--spacing) * 1000)
p-1.3 No genera CSS No genera CSS

La lista cerrada de 3.4 es esta, y conviene conocerla aunque uses 4.x, porque es la serie que mantiene el ritmo visual:

0 px 0.5 1 1.5 2 2.5 3 3.5 4 5 6 7 8 9 10 11 12 14 16 20 24 28 32 36 40 44 48 52 56 60 64 72 80 96

Barras de distintas anchuras; w-13 y w-150 aparecen en naranja por no existir en la versión 3.4

Figura 3. La escala aplicada a w-*. Las barras naranjas (w-13 y w-150) solo existen en 4.x.

⚠️ Importante: Que puedas escribir p-13 no significa que debas. Si usas todos los números, pierdes la ventaja de la escala. Reserva los valores que no están en la serie para los casos en que un diseño los pida de verdad.

Densidad local con --spacing 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) las utilidades llevan el valor escrito (padding: 1.5rem), así que no se puede reescalar una zona de la página. Si usas la LTS: no hay equivalente directo; define variantes a mano con clases distintas (p-6 frente a p-3) para cada densidad.

Como .p-6 es padding: calc(var(--spacing) * 6), y las variables se heredan, puedes redefinir la unidad en un elemento con una propiedad arbitraria. Todo lo que hay dentro se adapta:

<article class="w-64 rounded-lg border border-gray-200 p-6">
  <h3 class="font-bold">Portátil UltraBook 14</h3>
  <p class="mt-2 text-sm text-gray-600">Portátil ligero de 14 pulgadas, 16 GB RAM</p>
  <p class="mt-4 font-bold">899 €</p>
</article>

<article class="w-64 rounded-lg border border-gray-200 p-6 [--spacing:0.15rem]">
  <h3 class="font-bold">Portátil UltraBook 14</h3>
  <p class="mt-2 text-sm text-gray-600">Portátil ligero de 14 pulgadas, 16 GB RAM</p>
  <p class="mt-4 font-bold">899 €</p>
</article>

Dos tarjetas iguales; la segunda es más estrecha y tiene menos relleno

Figura 4. La misma tarjeta con --spacing por defecto y con 0.15rem. El relleno pasa de 24 a 14,4 px.

Fíjate en un efecto secundario: la segunda tarjeta también es más estrecha, porque w-64 usa la misma variable y pasa de 256 a 153,6 px. Si quieres un modo compacto que conserve el ancho, fija el ancho con algo que no dependa de la escala, como un valor arbitrario o un ancho con nombre (sección 8). Es una técnica potente para tablas y paneles densos, pero úsala con cuidado.


4. Relleno: p-*

Los sufijos

Tanto p- como m- admiten el mismo juego de sufijos:

Esquema de los sufijos t, r, b, l, x, y, s, e, bs y be, con los lados que afecta cada uno

Figura 5. Los sufijos de lado de las familias p- y m-.

Clase Lado CSS en 4.3
p-4 Los cuatro padding
px-4 Eje horizontal padding-inline
py-4 Eje vertical padding-block
pt-4, pr-4, pb-4, pl-4 Arriba, derecha, abajo, izquierda padding-top…
ps-4, pe-4 Inicio y fin del texto padding-inline-start, padding-inline-end
pbs-4, pbe-4 Inicio y fin del bloque padding-block-start, padding-block-end (sección 13)
<div class="bg-indigo-300 p-4"><div class="bg-white text-center font-mono text-sm">p-4</div></div>
<div class="bg-indigo-300 px-6"><div class="bg-white text-center font-mono text-sm">px-6</div></div>
<div class="bg-indigo-300 py-6"><div class="bg-white text-center font-mono text-sm">py-6</div></div>
<div class="bg-indigo-300 px-8 py-2"><div class="bg-white text-center font-mono text-sm">px-8 py-2</div></div>
<div class="bg-indigo-300 pt-6"><div class="bg-white text-center font-mono text-sm">pt-6</div></div>
<div class="bg-indigo-300 pr-6"><div class="bg-white text-center font-mono text-sm">pr-6</div></div>
<div class="bg-indigo-300 pb-6"><div class="bg-white text-center font-mono text-sm">pb-6</div></div>
<div class="bg-indigo-300 pl-6"><div class="bg-white text-center font-mono text-sm">pl-6</div></div>

Ocho cajas azules con el contenido blanco desplazado según la clase de relleno

Figura 6. El azul es el relleno y el blanco, el contenido.

Lo específico gana a lo general

Puedes combinar clases de distinta amplitud, sin importar el orden en que las escribas. Comprobado en el navegador con class="p-4 px-2 pl-1":

padding-top: 16px     (viene de p-4)
padding-right: 8px    (viene de px-2)
padding-left: 4px     (viene de pl-1)

Tailwind ordena la hoja de lo más amplio a lo más concreto, así que pl-1 queda detrás de px-2, y este detrás de p-4. Es el mismo principio del módulo 02: el orden del atributo class no decide nada, lo decide el orden en la hoja.

px-* y py-* son lógicas 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4), px-4 genera padding-left y padding-right, y lo mismo ocurre con py-*, mx-* y my-*. Si usas la LTS: en escritura horizontal el resultado es idéntico; solo cambia en escritura vertical, donde debes usar [padding-inline:1rem] para el mismo efecto.

/* Tailwind 3.4 */
.px-4 { padding-left: 1rem; padding-right: 1rem; }

/* Tailwind 4.3 */
.px-4 { padding-inline: calc(var(--spacing) * 4); }

En una página en español, o en árabe, no notarás nada: el eje inline sigue siendo horizontal. Solo cambia con writing-mode: vertical-rl, que verás en la sección 13.

Patrones habituales de relleno

Elemento Clases Por qué
Botón px-4 py-2 Más relleno horizontal: el texto respira y la altura sigue baja
Etiqueta px-2 py-0.5 Compacta
Tarjeta p-6 Igual por los cuatro lados
Celda de tabla px-4 py-3 Las filas se leen mejor con aire vertical
Sección de página px-4 py-8 Separación lateral en móvil, vertical amplia

⚠️ Importante: No hay relleno negativo. -p-4 no genera CSS en ninguna versión, ni tampoco -gap-2 o -w-4. El estándar CSS no los permite. Solo el margen puede ser negativo.


5. Margen: m-*

Los sufijos son los mismos: m, mx, my, mt, mr, mb, ml, ms, me. Lo interesante del margen son tres cosas que no tiene el relleno: auto, los negativos y el colapso.

Centrar y empujar con auto

Un margen auto se queda con todo el espacio libre. Con eso se resuelven dos patrones muy frecuentes:

<!-- Centrar un bloque: necesita una anchura menor que la del padre -->
<div class="mx-auto w-48 bg-indigo-200 p-2 font-mono text-sm">mx-auto</div>

<!-- Pegar un bloque a la derecha -->
<div class="ml-auto w-48 bg-indigo-200 p-2 font-mono text-sm">ml-auto</div>

<!-- En un flex: separar un grupo del resto -->
<div class="flex items-center bg-gray-100 p-2">
  <span class="font-bold">EMPRESA</span>
  <a href="#" class="ml-auto text-indigo-700">Catálogo</a>
  <a href="#" class="ml-4 text-indigo-700">Carrito</a>
</div>

Cuatro filas: bloque a la izquierda, centrado, a la derecha y barra de navegación con enlaces a la derecha

Figura 7. mx-auto centra el bloque, ml-auto lo empuja a la derecha, y en un contenedor flex separa grupos sin elementos vacíos.

El CSS ya lo conoces, aunque cambia ligeramente de una versión a otra:

/* Tailwind 3.4 */
.mx-auto { margin-left: auto; margin-right: auto; }

/* Tailwind 4.3 */
.mx-auto { margin-inline: auto; }

⚠️ Importante: mx-auto no hace nada en dos casos muy habituales. En un bloque sin anchura, porque ya ocupa todo y no queda espacio libre que repartir. Y en un elemento en línea como <span>, porque los márgenes horizontales auto no centran elementos en línea. Si no se centra, comprueba primero que tenga w-* o max-w-* y que su display sea block.

Márgenes negativos

Se escriben con un guion delante de toda la clase, no del número. Sirven para sacar un elemento de su sitio y solaparlo con otro:

<article class="rounded-lg border border-gray-200">
  <div class="h-24 rounded-t-lg bg-indigo-600"></div>
  <div class="px-6 pb-6">
    <div class="-mt-10 size-20 rounded-full border-4 border-white bg-orange-400"></div>
    <h3 class="mt-2 font-bold">Isabel Torres</h3>
    <p class="text-sm text-gray-600">Directora de Ventas</p>
  </div>
</article>
/* Tailwind 4.3 */
.-mt-10 {
  margin-top: calc(var(--spacing) * -10);
}
/* Con valor arbitrario */
.-mt-\[3px\] {
  margin-top: calc(3px * -1);
}
/* -mt-px no usa la escala */
.-mt-px {
  margin-top: -1px;
}

El colapso de márgenes

Es la causa de muchas sorpresas. Cuando dos márgenes verticales se tocan en un flujo de bloques, no se suman: se queda el mayor. Dentro de un contenedor flex o grid eso no ocurre.

<!-- En flujo de bloques -->
<div class="w-1/2 bg-gray-100">
  <div class="mb-8 bg-indigo-200 p-2 font-mono text-sm">mb-8</div>
  <div class="mt-4 bg-orange-200 p-2 font-mono text-sm">mt-4</div>
</div>

<!-- En un contenedor flex en columna -->
<div class="flex w-1/2 flex-col bg-gray-100">
  <div class="mb-8 bg-indigo-200 p-2 font-mono text-sm">mb-8</div>
  <div class="mt-4 bg-orange-200 p-2 font-mono text-sm">mt-4</div>
</div>

A la izquierda, una tarjeta con avatar solapado con margen negativo; a la derecha, los dos casos de colapso de márgenes

Figura 8. Margen negativo, y a la derecha el colapso: la separación del caso en bloque es menor que la del caso flex.

Separación medida: 32 px en el caso de bloque (gana mb-8), y 48 px en el flex (se suman).

El margen de un hijo también puede atravesar a su padre si este no tiene relleno ni borde, y entonces parece que el padre no contiene al hijo. Lo verás en la sección 7 al probar space-y-*: el margen del último hijo desaparece de la caja del padre si nada lo detiene.

💡 Consejo: Una regla que evita casi todos estos problemas: separa los hermanos desde el padre con gap-*, y no mezcles mb-* y mt-* en elementos contiguos. Si un componente necesita separarse del exterior, deja esa decisión a quien lo coloca, no al propio componente.


6. Separación entre hijos: gap-*

gap es una propiedad CSS de flex y grid. Pone espacio solo entre los hijos, nunca antes del primero ni después del último, y no depende de cuántos hijos haya ni de si alguno está oculto.

Clase CSS
gap-4 gap
gap-x-4 column-gap (entre columnas)
gap-y-4 row-gap (entre filas)
gap-px gap: 1px
<div class="flex gap-4 bg-gray-100">...</div>

<div class="grid grid-cols-3 gap-4 bg-gray-100">...</div>

<div class="grid grid-cols-3 gap-x-8 gap-y-2 bg-gray-100">...</div>

Fila flex con separación, rejilla con separación uniforme y rejilla con más separación horizontal que vertical

Figura 9. gap-4 en flex y en grid, y separación distinta por eje con gap-x-8 gap-y-2.

⚠️ Importante: gap-* necesita que el padre sea flex o grid (también funciona en columnas múltiples). En un <div> normal no hace nada y no da ningún error: es un fallo frecuente.


7. space-*: la opción antigua 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) el selector es > :not([hidden]) ~ :not([hidden]) y pone el margen delante de cada hijo salvo el primero; en 4.x es :where(> :not(:last-child)) y lo pone detrás. Si usas la LTS: las clases son las mismas, pero el hijo oculto que da problemas es el primero, y los márgenes propios del hijo no se pueden cambiar. Lo más seguro en ambas versiones es usar flex y gap-*.

space-x-* y space-y-* nacieron antes de que gap funcionase bien en flex en todos los navegadores. La clase se pone en el padre, pero lo que hace es dar margen a los hijos:

/* Tailwind 3.4 */
.space-y-4 > :not([hidden]) ~ :not([hidden]) {
  --tw-space-y-reverse: 0;
  margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
  margin-bottom: calc(1rem * var(--tw-space-y-reverse));
}

/* Tailwind 4.3 */
:where(.space-y-4 > :not(:last-child)) {
  --tw-space-y-reverse: 0;
  margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));
  margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)));
}

Hay dos cambios importantes:

  • Qué hijo se salta. En 3.4 se salta el primero y en 4.x el último. Si ese hijo no se muestra, el espacio sobrante aparece en un sitio distinto.
  • Especificidad. :where() vale cero, así que en 4.x una clase en el hijo, como mt-10, le gana. En 3.4 el selector tiene especificidad de tres clases y le gana a cualquier utilidad del hijo.

Lo hemos comprobado con la misma página compilada en las dos versiones. Cada lista lleva un borde para que el margen no atraviese al padre:

<!-- A: último oculto -->
<ul class="space-y-4 border-2 border-orange-400 bg-orange-100">
  <li class="bg-indigo-200 p-2">Uno</li>
  <li class="bg-indigo-200 p-2">Dos</li>
  <li class="hidden bg-indigo-200 p-2">Tres</li>
</ul>

<!-- B: primero oculto -->
<ul class="space-y-4 border-2 border-orange-400 bg-orange-100">
  <li class="hidden bg-indigo-200 p-2">Uno</li>
  <li class="bg-indigo-200 p-2">Dos</li>
  <li class="bg-indigo-200 p-2">Tres</li>
</ul>

<!-- C: un hijo con su propio margen -->
<ul class="space-y-4 border-2 border-orange-400 bg-orange-100">
  <li class="bg-indigo-200 p-2">Uno</li>
  <li class="mt-10 bg-indigo-200 p-2">Dos</li>
  <li class="bg-indigo-200 p-2">Tres</li>
</ul>

<!-- D: la alternativa con gap -->
<ul class="flex flex-col gap-4 border-2 border-orange-400 bg-orange-100">
  <li class="bg-indigo-200 p-2">Uno</li>
  <li class="bg-indigo-200 p-2">Dos</li>
  <li class="hidden bg-indigo-200 p-2">Tres</li>
</ul>

Las cuatro listas compiladas en Tailwind 3.4 arriba y en 4.3 abajo, con el espacio sobrante en lugares distintos

Figura 10. La misma página en las dos versiones. El caso con gap-4 es el único que se ve igual.

Lista Altura en 3.4 Altura en 4.3 Qué ocurre
A: último oculto 84 px 100 px En 4.3 sobran 16 px abajo
B: primero oculto 100 px 84 px En 3.4 sobran 16 px arriba
C: hijo con mt-10 132 px 156 px En 3.4 el mt-10 se ignora
D: gap-4 84 px 84 px Correcto en las dos

Otra debilidad de space-* es el salto de línea. Al basarse en márgenes, no sabe nada de filas, y con flex-wrap deja la segunda línea sin separación vertical y descuadrada:

<div class="flex w-64 flex-wrap space-x-4 bg-orange-200">...</div>

<div class="flex w-64 flex-wrap gap-x-4 gap-y-2 bg-orange-200">...</div>

Dos listas de categorías en varias líneas: con space-x las filas se pegan; con gap quedan bien separadas

Figura 11. Arriba, space-x-4 con flex-wrap. Abajo, gap-x-4 gap-y-2.

Valores negativos e inversión

space-* tiene dos cosas que gap-* no tiene. Admite negativos (-space-x-3), útiles para pilas de avatares solapados, ya que gap no puede ser negativo. Y tiene space-x-reverse y space-y-reverse, para listas con flex-row-reverse o flex-col-reverse.

Cuál elegir

Situación Elige Motivo
Hijos de flex o grid gap-* Sin efectos secundarios
Filas que pueden saltar de línea gap-x-* y gap-y-* space-* no entiende de filas
Texto en flujo normal (párrafos seguidos) space-y-* No obliga a cambiar el display
Hijos solapados -space-x-* o -ms-* gap no admite negativos
Un solo elemento con separación especial mt-* Es una excepción local

La guía oficial de actualización a 4.0 recomienda pasar a flex con gap-* si el cambio de selectores te da problemas. Es la opción por defecto en este curso.


8. Anchura: w-*

La familia w-* tiene cinco tipos de valor: la escala, fracciones, nombres, palabras clave y unidades del viewport. Las dos primeras son de uso diario.

Tipo Ejemplos CSS
Escala w-64 calc(var(--spacing) * 64)
Fracción w-1/2, w-5/12 calc(1 / 2 * 100%)
Porcentaje completo w-full 100%
Nombre w-md var(--container-md)
Palabra clave w-auto, w-min, w-max, w-fit auto, min-content…
Viewport w-screen, w-dvw, w-svw, w-lvw 100vw, 100dvw…
Un píxel w-px 1px

Fracciones

Una fracción es un porcentaje del padre. En 3.4 se escribe el resultado (width: 50%, width: 41.666667%), y en 4.x se calcula en el propio CSS (calc(5 / 12 * 100%)).

<div class="w-full bg-indigo-200 p-2">w-full</div>
<div class="w-3/4 bg-indigo-200 p-2">w-3/4</div>
<div class="w-2/3 bg-indigo-200 p-2">w-2/3</div>
<div class="w-1/2 bg-indigo-200 p-2">w-1/2</div>
<div class="w-1/3 bg-indigo-200 p-2">w-1/3</div>

<div class="flex gap-2">
  <div class="w-1/3 bg-orange-200 p-2">w-1/3</div>
  <div class="w-2/3 bg-orange-200 p-2">w-2/3</div>
</div>

<div class="flex gap-2">
  <div class="w-[calc(100%/3-0.25rem)] bg-orange-200 p-2">w-[...]</div>
  <div class="flex-1 bg-orange-200 p-2">flex-1</div>
</div>

Barras de distintas anchuras en fracciones y barras de anchos con nombre, de 3xs a 2xl

Figura 12. Arriba, fracciones del padre. Abajo, la serie de anchos con nombre.

⚠️ Importante: w-1/3 + w-2/3 suman el 100 % del padre, y el gap añade 8 px más. En un contenedor flex los hijos encogen un poco para que quepa (es lo que ves en la figura), pero con shrink-0 se saldrían. Para columnas con separación es mejor flex-1 o una rejilla con grid-cols-*.

Fracciones libres 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) solo hay denominadores 2, 3, 4, 5, 6 y 12, y w-7/13 no genera CSS. Si usas la LTS: escribe un valor arbitrario como w-[53.846%] o w-[calc(7/13*100%)].

En 4.x vale cualquier fracción de enteros, como w-7/13.

Anchos con nombre 🆕 v4.0

🆕 Desde Tailwind 4.0. No existe en la LTS (Tailwind 3.4), donde esos nombres solo funcionan con max-w-*. Si usas la LTS: convierte a la escala (w-80 para w-xs, w-96 para w-sm) o usa un valor arbitrario como w-[28rem] para w-md.

En 4.x, w-*, min-w-* y max-w-* leen los nombres del espacio --container-* del tema. Solo hay versión para anchos: h-md no existe.

Nombre rem px Nombre rem px
3xs 16 256 2xl 42 672
2xs 18 288 3xl 48 768
xs 20 320 4xl 56 896
sm 24 384 5xl 64 1024
md 28 448 6xl 72 1152
lg 32 512 7xl 80 1280
xl 36 576      

Las dos primeras filas son también novedad de 4.0, incluso para max-w-*: en 3.4 no existen max-w-3xs ni max-w-2xs.

Palabras clave: el ancho según el contenido

<div class="w-auto bg-indigo-200 p-2"><code>w-auto</code> ocupa todo el ancho disponible</div>
<div class="w-min bg-indigo-200 p-2"><code>w-min</code> se estrecha hasta la palabra más larga</div>
<div class="w-max bg-indigo-200 p-2"><code>w-max</code> no parte nunca el texto en líneas</div>
<div class="w-fit bg-indigo-200 p-2"><code>w-fit</code> se ajusta a lo que necesita</div>
<div class="w-fit bg-orange-200 p-2"><code>w-fit</code> pero si el texto es tan largo que no cabe en una sola línea, se limita al ancho disponible</div>
<div class="w-max bg-orange-200 p-2"><code>w-max</code> pero si el texto es tan largo que no cabe en una línea, sigue en una línea y se sale</div>

Seis cajas que muestran w-auto, w-min, w-max y w-fit; la última w-max se sale del contenedor gris

Figura 13. Las dos cajas naranjas muestran la diferencia entre w-fit y w-max: la primera respeta al padre y la segunda no.

Clase Valor Comportamiento
w-auto auto Valor por defecto. Un bloque ocupa todo el ancho
w-min min-content Lo mínimo sin partir palabras
w-max max-content Todo en una línea, aunque se salga
w-fit fit-content Lo que necesite, sin pasar del disponible

w-fit es la más útil de las cuatro. Sirve, por ejemplo, para que un bloque se ajuste a su texto y se pueda centrar con mx-auto.

w-screen y la barra de desplazamiento

w-screen es 100vw, y 100vw incluye la barra de desplazamiento vertical si esta ocupa espacio. En Windows, con barras clásicas, un elemento w-screen dentro de una página larga provoca un desplazamiento horizontal de unos pocos píxeles. Casi siempre lo que quieres es w-full, porque el padre ya excluye esa barra.


9. Altura: h-*

h-* comparte los valores de w-*, salvo los nombres:

Tipo Ejemplos
Escala h-16, h-96
Fracciones h-1/2
Palabras clave h-auto, h-full, h-min, h-max, h-fit
Viewport h-screen, h-dvh, h-svh, h-lvh
Un píxel h-px

La gran diferencia entre anchura y altura es que el flujo normal ocupa todo el ancho, pero solo el alto que necesita. Por eso los porcentajes de altura se comportan distinto.

Por qué h-full a veces no hace nada

Un porcentaje de altura se calcula sobre la altura del padre. Si el padre tiene height: auto, su altura depende de sus hijos, y el navegador no puede resolver el porcentaje: lo trata como auto.

<div class="w-1/2 bg-gray-100">
  <div class="h-full bg-orange-200 p-2 font-mono text-sm">h-full, sin altura en el padre</div>
</div>

<div class="h-32 w-1/2 bg-gray-100">
  <div class="h-full bg-indigo-200 p-2 font-mono text-sm">h-full, padre con h-32</div>
</div>

Dos cajas: la de la izquierda solo mide lo que su texto; la de la derecha ocupa toda la altura del padre

Figura 14. A la izquierda, h-full se queda en 36 px. A la derecha, llena los 128 px del padre.

Para llenar el alto del padre sin definir su altura, usa flex o grid: el valor por defecto stretch estira a los hijos sin ningún porcentaje. Lo verás en Flexbox.

h-lh, min-h-lh y max-h-lh: una línea de texto 🆕 v4.2

🆕 Desde Tailwind 4.2. No existe en la LTS (Tailwind 3.4). Si usas la LTS: escribe un valor arbitrario como h-[1lh], que genera lo mismo.

lh es una unidad CSS que vale una altura de línea del elemento. Sirve para reservar sitio en líneas de texto, o para que un adorno mida lo mismo que el texto:

<p class="text-lg"><span class="inline-block h-lh w-1 bg-indigo-600 align-top"></span> Texto normal</p>
<p class="text-3xl"><span class="inline-block h-lh w-1 bg-indigo-600 align-top"></span> Texto grande</p>

La barra mide 28 px junto al text-lg y 36 px junto al text-3xl, sin que tengas que calcular nada. Con min-h-lh un párrafo vacío conserva la altura de una línea y la maquetación no salta cuando llega el texto.

⚠️ Importante: No confundas la fecha: comprobado instalando las versiones, h-lh falta en 4.1.0 y aparece en 4.2.0. Tampoco existe w-lh.


10. El viewport en móviles: vh, svh, lvh y dvh

En un ordenador, la ventana mide lo que mide. En un móvil, la barra de direcciones aparece y desaparece al desplazarte, de modo que el viewport tiene dos alturas. La antigua unidad vh no se ajusta a esto: en navegadores móviles equivale a la altura grande, con la barra oculta. Por eso una sección h-screen se corta al cargar la página, y su botón inferior queda fuera de la vista.

CSS añadió tres unidades para resolverlo:

Clase Valor Significado
h-screen 100vh Clásica. En móvil, como lvh
h-svh 100svh Small: altura con la barra visible, la más pequeña
h-lvh 100lvh Large: altura con la barra oculta, la más grande
h-dvh 100dvh Dynamic: la altura real en cada momento

Dos teléfonos: con la barra visible y con la barra oculta, y lo que mide cada unidad en cada caso

Figura 15. svh y lvh son fijas. dvh cambia cuando la barra aparece o desaparece.

Existen las mismas variantes para min-h-*, max-h-* y para anchos (w-dvw, w-svw, w-lvw), y todas funcionan en 3.4.

Quieres Usa Por qué
Portada o sección a pantalla completa min-h-svh Se ve entera al cargar y no salta al desplazarte
Página con pie al fondo min-h-dvh Si hay poco contenido, llena lo visible
Panel de aplicación con desplazamiento interno h-dvh Se ajusta a la zona visible real
Modal a pantalla completa h-dvh Sus botones siempre quedan a la vista

💡 Consejo: dvh puede provocar pequeños saltos, porque el navegador recalcula el tamaño mientras se oculta la barra. Si un elemento contiene mucho contenido debajo y notas el salto, prueba svh.

Un error que parece buena idea

Con unidades nuevas, en CSS normal es habitual escribir dos declaraciones para que un navegador antiguo use la primera. Con clases eso no funciona:

<!-- No sirve: h-screen siempre gana -->
<div class="h-screen h-dvh">...</div>

La hoja la ordena Tailwind, y lo que queda en ambas versiones, medido en el navegador, es este orden:

.h-dvh  .h-screen  .min-h-dvh  .min-h-screen

h-screen queda detrás de h-dvh y por tanto gana. Si de verdad necesitas compatibilidad con navegadores que no conocen dvh (solo puede ocurrir en 3.4, porque los navegadores que exige 4.x ya la entienden), usa una variante condicional:

<div class="h-screen supports-[height:100dvh]:h-dvh">...</div>
@supports (height:100dvh) {
  .supports-\[height\:100dvh\]\:h-dvh {
    height: 100dvh;
  }
}

Dos patrones de página

El pie al fondo: una columna de al menos la altura de la pantalla, cuyo main se queda con lo que sobre. Si el contenido crece, la página crece.

<body class="flex min-h-dvh flex-col bg-gray-50 text-gray-900">
  <header class="flex h-16 items-center bg-indigo-700 px-6 font-bold text-white">EMPRESA</header>

  <main class="mx-auto w-full max-w-3xl flex-1 px-6 py-8">
    <h1 class="text-2xl font-bold">Tu carrito está vacío</h1>
    <p class="mt-2 text-gray-600">Vuelve al catálogo para añadir productos.</p>
  </main>

  <footer class="bg-gray-200 px-6 py-4 text-sm text-gray-700">© EMPRESA. Pie siempre abajo aunque haya poco contenido.</footer>
</body>

El panel de aplicación: altura fija a la pantalla, y solo se desplaza la zona central. La solución completa está en el ejercicio 10.

Arriba, una página con el pie al fondo; abajo, un panel con barra lateral y lista de pedidos con desplazamiento interno

Figura 16. Los dos patrones de altura completa. El panel se desplaza por dentro: la página en sí no tiene barra de desplazamiento.


11. Tamaño a la vez: size-*

Cuando un elemento es cuadrado, size-* evita repetir w-* y h-*:

.size-16 {
  width: calc(var(--spacing) * 16);
  height: calc(var(--spacing) * 16);
}

Admite la escala (size-16), size-px, fracciones (size-1/2), size-full, size-auto, size-min, size-max y size-fit. Es lo natural para iconos y avatares.

<div class="size-6 rounded-full bg-indigo-600"></div>
<div class="size-10 rounded-full bg-indigo-600"></div>
<div class="size-16 rounded-full bg-indigo-600"></div>
<div class="size-24 rounded-full bg-orange-400"></div>

<div class="flex size-24 items-center justify-center rounded-lg bg-indigo-100">
  <div class="size-1/2 rounded bg-indigo-600"></div>
</div>

Cuatro círculos de tamaños crecientes y un cuadrado con otro cuadrado centrado

Figura 17. Avatares con size-6 a size-24, y un size-1/2 dentro de un size-24.

Si combinas size-* con h-* o w-*, la clase más concreta gana, sin importar el orden: size-8 h-4 da 32 × 16 px en las dos versiones, y w-full size-8 conserva el w-full.

size-* llegó en la versión 3.4, así que puedes usarlo en la LTS.

size-dvh y compañía 🆕 v4.0

🆕 Desde Tailwind 4.0. No existe en la LTS (Tailwind 3.4), ni tampoco w-dvh o h-dvw. Si usas la LTS: escribe size-[100dvh], w-[100dvh] o h-[100dvw].

4.x añade size-dvh, size-dvw, size-svh, size-svw, y las combinaciones cruzadas w-dvh (anchura igual a la altura del viewport) y h-dvw. No hay size-screen.


12. Límites: min-* y max-*

Las clases min-w-*, max-w-*, min-h-* y max-h-* ponen límites sin fijar un tamaño. Esto permite que el elemento siga siendo flexible. Es la forma adaptable de decir “ancho total, pero sin pasarse”:

<main class="mx-auto w-full max-w-3xl px-6">...</main>

En un móvil de 400 px ocupa los 400 px. En un monitor de 1920 px se queda en 768 px y se centra. No hace falta ninguna variante responsive.

Familia Escala Palabras clave Nombres
max-w-* Sí none, full, min, max, fit, prose, screen-sm… xs a 7xl (3xs, 2xs desde 4.0)
min-w-* Sí 0, full, min, max, fit Solo desde 4.0
max-h-* Sí none, full, min, max, fit, screen, dvh, svh, lvh No
min-h-* Sí 0, full, min, max, fit, screen, dvh, svh, lvh No

Quién gana: width, max-width o min-width

CSS aplica siempre el mismo orden: primero width, luego lo limita max-width, y min-width tiene la última palabra. No depende de Tailwind. Comprobado con w-40 max-w-24 min-w-32: mide 128 px en las dos versiones, que es lo que pide min-w-32.

max-w-prose y la longitud de línea

max-w-prose es max-width: 65ch: unos 65 caracteres por línea, una longitud cómoda de lectura. Va ligada al tamaño de letra del propio elemento.

<p class="max-w-prose bg-indigo-100 p-2 text-sm">...</p>

max-w-screen-*

Funciona en las dos versiones y limita a un punto de ruptura, con una diferencia de unidad: max-w-screen-md es 768px en 3.4 y var(--breakpoint-md) (48 rem) en 4.x. La documentación de 4.x prefiere la forma max-w-(--breakpoint-md), que verás en la sección 14.

El misterio de min-w-0

Es uno de los errores más repetidos con flex. Un hijo flex tiene min-width: auto, lo que le impide encoger por debajo del ancho de su contenido. Si dentro hay un truncate (que no parte el texto), el contenido mide todo lo que mide la frase, y el hijo no encoge.

<!-- Bien: el propio hijo tiene truncate, que incluye overflow: hidden -->
<div class="flex gap-2 bg-gray-100 p-2 outline outline-gray-400">
  <div class="size-8 shrink-0 bg-indigo-600"></div>
  <p class="truncate">Portátil de 16 pulgadas con gráfica dedicada para juegos y edición de vídeo</p>
</div>

<!-- Mal: un envoltorio sin límite -->
<div class="flex gap-2 bg-gray-100 p-2 outline outline-gray-400">
  <div class="size-8 shrink-0 bg-orange-500"></div>
  <div>
    <p class="truncate">Portátil de 16 pulgadas con gráfica dedicada para juegos y edición de vídeo</p>
  </div>
</div>

<!-- Bien: min-w-0 permite encoger al envoltorio -->
<div class="flex gap-2 bg-gray-100 p-2 outline outline-gray-400">
  <div class="size-8 shrink-0 bg-indigo-600"></div>
  <div class="min-w-0">
    <p class="truncate">Portátil de 16 pulgadas con gráfica dedicada para juegos y edición de vídeo</p>
  </div>
</div>

Un párrafo largo sin límite y otro con max-w-prose, y tres filas con texto truncado, de las cuales la segunda desborda

Figura 18. Arriba, max-w-prose frente a un texto sin límite. Abajo, la segunda fila desborda porque le falta min-w-0: su contenido mide 515 px en un contenedor de 368 px.

El mismo problema ocurre en vertical: un hijo de una columna flex tiene min-height: auto. El mínimo automático desaparece si el propio hijo lleva overflow-*, y por eso la primera fila de la figura funciona sin min-w-0: truncate incluye overflow: hidden. Pero si la zona con desplazamiento es un envoltorio más interno, el hijo flex necesita min-h-0. Medido en una columna de 160 px con un contenido de 384 px:

<!-- Funciona: main mide 160 px y se desplaza -->
<main class="flex-1 overflow-y-auto">...</main>

<!-- No funciona: main crece hasta 384 px y no hay desplazamiento -->
<main class="flex-1">
  <div class="h-full overflow-y-auto">...</div>
</main>

<!-- Funciona: main mide 160 px, y se desplaza el envoltorio -->
<main class="min-h-0 flex-1">
  <div class="h-full overflow-y-auto">...</div>
</main>

overflow-* se estudia en Layout y posicionamiento.

min-w-auto y min-h-auto 🆕 v4.0

🆕 Desde Tailwind 4.0. No existen en la LTS (Tailwind 3.4). Si usas la LTS: usa valores arbitrarios: min-w-[auto] y min-h-[auto].

Restablecen el valor por defecto de un hijo flex o grid. Solo los necesitarás para anular un min-w-0 en un punto de ruptura concreto.


13. Propiedades lógicas

Hasta ahora has usado casi siempre lados físicos: izquierda, derecha, arriba y abajo. Pero una interfaz en árabe o hebreo se lee de derecha a izquierda, y lo que en español es “antes del texto” allí está en el lado contrario. Las propiedades lógicas no hablan de lados de la pantalla, sino del sentido de la escritura:

  • Eje inline: en el que avanzan las letras de una línea. En español, de izquierda a derecha.
  • Eje block: en el que se apilan las líneas. En español, de arriba abajo.
  • Inicio (start) y fin (end): dentro de cada eje.

Tres cajas con los ejes inline y block en escritura de izquierda a derecha, de derecha a izquierda y vertical

Figura 19. El significado de s, e, bs y be depende de la escritura.

ps-*, pe-*, ms-* y me-*

Funcionan en las dos versiones y son las que más se usan:

<div dir="rtl">
  <div class="border-l-4 border-orange-500 bg-white pl-4">pl-4</div>
  <div class="border-s-4 border-indigo-600 bg-white ps-4">ps-4</div>
  <div class="flex items-center bg-white p-2"><span class="size-6 bg-orange-500"></span><span class="ml-4">ml-4</span></div>
  <div class="flex items-center bg-white p-2"><span class="size-6 bg-indigo-600"></span><span class="ms-4">ms-4</span></div>
  <div class="flex space-x-4 bg-white p-2"><span class="bg-indigo-200 px-2">space-x-4</span><span class="bg-indigo-200 px-2">B</span><span class="bg-indigo-200 px-2">C</span></div>
</div>

Comparativa en 3.4 y 4.3 de una columna de izquierda a derecha y otra de derecha a izquierda; en esta, las versiones físicas fallan

Figura 20. En la columna rtl, las filas naranjas usan lados físicos y se quedan mal: la barra sigue a la izquierda y el texto se pega al cuadrado. Las de lados lógicos se adaptan solas.

Fíjate también en la última fila de la columna rtl. En 3.4, space-x-4 usa margin-left, y la separación entre “space-x-4” y “B” desaparece, salvo que añadas rtl:space-x-reverse (la variante rtl: se estudia en Variantes y estados). En 4.x usa margin-inline-end y funciona sin más.

💡 Consejo: Acostúmbrate a escribir ms-*, me-*, ps-* y pe-* desde el principio. En español se ven igual que los físicos, no cuestan nada más, y tu interfaz queda lista para cualquier idioma. Los ejemplos del resto del curso los usan cuando el lado depende del sentido del texto.

No mezcles físicas y lógicas sobre el mismo lado

Con ml-6 ms-2, las dos clases afectan al mismo margen en español. Quién gana depende del orden en la hoja, y ese orden es distinto en cada versión:

Clases Tailwind 3.4 Tailwind 4.3
ml-6 ms-2 margin-left: 8px (gana ms-2) margin-left: 24px (gana ml-6)

Una migración de versión podría cambiar el aspecto de tu página sin ningún aviso. Elige una u otra en cada elemento.

Escritura vertical: px-* cambia de sentido 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) px-* y py-* son siempre horizontal y vertical en la pantalla. En 4.x siguen los ejes de la escritura. Si usas la LTS: si tu interfaz emplea escritura vertical, usa [padding-inline:2rem] y [padding-block:2rem] para obtener el comportamiento de 4.x.

Tailwind no trae ninguna clase para writing-mode, así que se escribe con una propiedad arbitraria. Es un caso poco común, útil para títulos laterales o texto en japonés o chino:

<div class="h-48 w-20 bg-indigo-400 px-8 [writing-mode:vertical-rl]">...</div>
<div class="h-48 w-20 bg-indigo-400 py-8 [writing-mode:vertical-rl]">...</div>
<div class="h-48 w-20 bg-indigo-400 ps-8 [writing-mode:vertical-rl]">...</div>
<div class="h-48 w-20 bg-indigo-400 pt-8 [writing-mode:vertical-rl]">...</div>

Las cuatro cajas de escritura vertical en 3.4 y en 4.3; px-8 y py-8 intercambian su efecto

Figura 21. El azul es el relleno. Observa que px-8 y py-8 se intercambian entre versiones, mientras que ps-8 y pt-8 no cambian.

Clase Relleno en 3.4 Relleno en 4.3
px-8 Izquierda y derecha Arriba y abajo
py-8 Arriba y abajo Izquierda y derecha
ps-8 Arriba Arriba
pt-8 Arriba Arriba

pbs-*, pbe-*, mbs-* y mbe-* 🆕 v4.2

🆕 Desde Tailwind 4.2. No existe en la LTS (Tailwind 3.4). Si usas la LTS: en escritura horizontal usa pt-*, pb-*, mt-* y mb-*, que hacen lo mismo. Si necesitas la versión lógica, escribe [padding-block-start:1rem].

Completan los sufijos del eje block. Son la pareja de ps-*/pe-*, y admiten negativos y auto igual que el resto de márgenes (-mbs-2, mbs-auto).

Clase CSS
pbs-4 padding-block-start
pbe-4 padding-block-end
mbs-4 margin-block-start
mbe-4 margin-block-end

inline-* y block-* 🆕 v4.2

🆕 Desde Tailwind 4.2. No existe en la LTS (Tailwind 3.4). Si usas la LTS: en escritura horizontal usa w-*, h-*, min-w-*, max-w-*, min-h-* y max-h-*. Si necesitas la versión lógica, escribe [inline-size:14rem].

Son la versión lógica de w-* y h-*. Admiten los mismos valores (escala, fracciones, full, auto, fit, dvw…):

Clase CSS En español equivale a
inline-56 inline-size w-56
block-32 block-size h-32
min-inline-4, max-inline-4 min-inline-size, max-inline-size min-w-4, max-w-4
min-block-32, max-block-32 min-block-size, max-block-size min-h-32, max-h-32

⚠️ Importante: No las confundas con inline y block, las clases de display. block a secas es display: block, y block-4 es una altura. También son distintas inline-block (display) e inline-full (ancho completo). El nombre se presta a errores al leer el código.

El interés de estas clases se ve al cambiar de escritura. Mismas clases, distinta orientación:

<article class="inline-56 min-block-32 bg-indigo-100 pbs-4 pbe-8 ps-6 pe-2">
  <h3 class="mbe-2 font-bold">Horizontal</h3>
  <p>Portátil ligero de 14 pulgadas, 16 GB RAM</p>
</article>

<article class="inline-56 min-block-32 bg-orange-100 pbs-4 pbe-8 ps-6 pe-2 [writing-mode:vertical-rl]">
  <h3 class="mbe-2 font-bold">Vertical</h3>
  <p>Portátil ligero de 14 pulgadas, 16 GB RAM</p>
</article>

Dos tarjetas con las mismas clases: una en horizontal y otra girada en escritura vertical

Figura 22. La tarjeta horizontal mide 224 × 128 px y la vertical, 128 × 224 px. El relleno se reparte siguiendo la escritura.

Medidas en el navegador. Relleno de la horizontal: arriba 16, abajo 32, izquierda 24, derecha 8. Relleno de la vertical: arriba 24, abajo 8, izquierda 32, derecha 16.


14. Valores a medida

La escala no lo cubre todo. Tienes tres vías, que ya presentó el módulo 02: arbitrarios, variables y tema.

Valores arbitrarios

Funcionan igual en las dos versiones:

Clase CSS
w-[37px] width: 37px
m-[5%] margin: 5%
h-[50dvh] height: 50dvh
w-[calc(100%-2rem)] width: calc(100% - 2rem)
p-[10px_20px] padding: 10px 20px
gap-[2cqw] gap: 2cqw

El guion bajo equivale a un espacio. Dentro de calc() Tailwind añade los espacios que exige CSS alrededor de + y -.

Variables CSS 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) se escribe con corchetes, w-[--ancho-lateral], y los paréntesis no generan nada. Si usas la LTS: sustituye w-(--ancho-lateral) por w-[--ancho-lateral].

<div class="w-(--ancho-lateral) bg-orange-200 p-2 [--ancho-lateral:18rem]">w-(--ancho-lateral)</div>
.w-\(--ancho-lateral\) {
  width: var(--ancho-lateral);
}

Mide 288 px en el navegador. Recuerda del módulo 02 que p-[--relleno] en 4.x compila a padding: --relleno, que no es válido.

También con paréntesis puedes aprovechar los puntos de ruptura del tema: max-w-(--breakpoint-md).

Valores propios en el tema 🔄 v4.0

🔄 Cambia en Tailwind 4.0. En la LTS (Tailwind 3.4) la escala y los anchos se definen en tailwind.config.js, con theme.extend.spacing y theme.extend.maxWidth. Si usas la LTS: define los mismos nombres ahí, como se muestra abajo.

Si un valor se repite y tiene un significado, dale nombre en vez de copiarlo como arbitrario:

@import "tailwindcss";

@theme {
  --spacing-tarjeta: 1.75rem;
  --container-ficha: 22rem;
}
<div class="max-w-ficha bg-indigo-200 p-tarjeta">max-w-ficha p-tarjeta</div>

En el navegador, el relleno es de 28 px y el ancho máximo de 352 px. El equivalente en 3.4:

// tailwind.config.js
export default {
  content: ["./*.html"],
  theme: {
    extend: {
      spacing: { tarjeta: "1.75rem" },
      maxWidth: { ficha: "22rem" },
    },
  },
};

⚠️ Importante: Evita usar el mismo nombre en --spacing-* y en --container-*. Lo descubrimos preparando este módulo: con --spacing-tarjeta: 1.75rem y --container-tarjeta: 22rem, la clase max-w-tarjeta dio max-width: 28px, porque --spacing-* tiene prioridad. No hay ningún aviso. El tema se estudia a fondo en Personalizar el tema.


15. Caso real: el catálogo de la tienda

Vamos a montar el catálogo de EMPRESA con lo que has aprendido. Fíjate en que no hay ni un solo valor arbitrario.

<!doctype html>
<html lang="es">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Catálogo de EMPRESA</title>
  <link rel="stylesheet" href="/app.css">
</head>
<body class="flex min-h-dvh flex-col bg-gray-50 text-gray-900">

  <header class="bg-white shadow-sm">
    <div class="mx-auto flex h-16 max-w-6xl items-center gap-6 px-4">
      <span class="text-lg font-bold text-indigo-700">EMPRESA</span>
      <nav class="ms-auto flex gap-6 text-sm">
        <a href="#" class="hover:text-indigo-700">Catálogo</a>
        <a href="#" class="hover:text-indigo-700">Ofertas</a>
        <a href="#" class="hover:text-indigo-700">Contacto</a>
      </nav>
      <div class="flex size-9 items-center justify-center rounded-full bg-indigo-600 text-sm font-bold text-white">IT</div>
    </div>
  </header>

  <main class="mx-auto w-full max-w-6xl flex-1 px-4 py-8">
    <div class="max-w-prose">
      <h1 class="text-3xl font-bold">Catálogo</h1>
      <p class="mt-2 text-gray-600">Equipos y periféricos para tu oficina, con envío en 24 horas y garantía de tres años en todos los portátiles.</p>
    </div>

    <section class="mt-8 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
      <article class="flex flex-col rounded-lg bg-white p-6 shadow-sm">
        <div class="h-32 w-full rounded bg-indigo-100"></div>
        <p class="mt-4 text-xs font-semibold text-indigo-600 uppercase">Portátiles</p>
        <h2 class="mt-1 text-lg font-bold">Portátil UltraBook 14</h2>
        <p class="mt-2 text-sm text-gray-600">Portátil ligero de 14 pulgadas, 16 GB RAM</p>
        <div class="mt-auto flex items-center justify-between pt-4">
          <span class="text-xl font-bold">899 €</span>
          <button class="rounded bg-indigo-600 px-4 py-2 text-sm text-white">Añadir</button>
        </div>
      </article>
      <!-- Otras dos tarjetas iguales: Portátil Gaming 16 y Ratón inalámbrico -->
    </section>
  </main>

  <footer class="mt-8 bg-gray-200 px-4 py-6 text-center text-sm text-gray-700">© EMPRESA</footer>
</body>
</html>

El catálogo con tres tarjetas en escritorio, y en móvil con una sola columna

Figura 23. El catálogo a 1100 px y a 400 px de ancho.

Las decisiones de espaciado y tamaño, una a una:

Clases Dónde Por qué
flex min-h-dvh flex-col <body> Patrón de pie al fondo
max-w-6xl mx-auto px-4 Cabecera y main El contenido se alinea en la misma columna. px-4 evita que se pegue al borde en móvil
w-full main Hijo de un flex en columna con márgenes auto: sin w-full se encogería a su contenido
flex-1 main Se queda con la altura sobrante y empuja el pie
h-16 Cabecera Altura fija, sin depender del tamaño de letra
ms-auto <nav> Empuja los enlaces y el avatar al final de la fila; funcionaría también en RTL
size-9 Avatar Es cuadrado
max-w-prose Introducción Líneas cómodas de leer
gap-6 Rejilla Separación uniforme sin márgenes en las tarjetas
p-6 Tarjeta Relleno uniforme
mt-1, mt-2, mt-4 Dentro de la tarjeta Lo relacionado se acerca: 4 px entre categoría y nombre, 16 px hasta la imagen
mt-auto Fila de precio Se pega abajo aunque las descripciones tengan distinta longitud

📘 Concepto: La proximidad comunica. Los elementos relacionados deben estar más cerca entre sí que de los demás. Los valores mt-1, mt-2 y mt-4 de la tarjeta forman una jerarquía: es la idea que guía cualquier decisión de espaciado.

⚠️ Importante: El detalle de w-full en main es un error muy común. mx-auto en un hijo de flex anula el estiramiento, y el main pasa a medir lo que mida su contenido. Si tu contenedor central sale estrecho, revisa esto.


16. Errores frecuentes y depuración

Síntoma Causa probable Solución
mx-auto no centra Sin ancho, o elemento en línea Añade w-*/max-w-*, o block
gap-4 no hace nada El padre no es flex ni grid Añade flex o grid
h-full no llena Padre con altura auto Dale altura al padre o usa flex/grid
Barra horizontal en toda la página w-screen, o algo con w-max Usa w-full; busca lo que se sale
Texto truncate desborda en flex Falta min-w-0 Añade min-w-0 en el hijo flex
Zona con overflow-y-auto no se desplaza Está dentro de un hijo flex sin min-h-0 Añade min-h-0 a ese hijo
El pie se corta en el móvil h-screen Usa h-dvh o min-h-dvh
Hay menos espacio del esperado Colapso de márgenes Usa gap-*
Espacio de más al final de una lista space-y-* con el último hijo oculto Usa gap-*
Mi mt-* no hace nada en un hijo space-y-* en 3.4 Usa gap-*, o cambia el margen del padre
p-13 o w-md no aplica Clase de 4.x en 3.4 Consulta las cajas de este módulo
-p-4 no aplica No existe relleno negativo Usa un margen negativo en el hijo
size-4 no aplica a un <span> Elemento en línea Añade inline-block o block
max-w-tarjeta da un valor absurdo Nombre repetido en --spacing-* Cambia el nombre en el tema

Para localizar lo que causa un desbordamiento horizontal, pega esto en la consola del navegador:

const ancho = document.documentElement.clientWidth;
for (const elemento of document.querySelectorAll("*")) {
  if (elemento.getBoundingClientRect().right > ancho) console.log(elemento);
}

Muestra los elementos cuyo borde derecho pasa del ancho visible. Para repasar el JavaScript usado, tienes el Curso de JavaScript.


17. Ejercicios

Los ejercicios se hacen en tu proyecto de Vite. Cada uno admite una página propia. Los resueltos se han compilado y comprobado en el navegador.

Ejercicio 1: Predice el resultado

Sin ejecutar nada, responde:

  1. ¿Cuántos píxeles mide p-5? ¿Y -mt-px?
  2. ¿Qué anchura final tiene un <div> con w-40 max-w-24 min-w-32?
  3. Un <div> con w-48 p-6 border-8 y otro igual con box-content: ¿cuánto mide cada uno de ancho?
  4. Dos bloques seguidos, el primero con mb-8 y el segundo con mt-4: ¿qué separación queda? ¿Y si el padre es flex flex-col?
Ver solución
  1. p-5 son 5 × 4 = 20 px. -mt-px es -1 px: px no usa la escala.
  2. 128 px. max-w-24 reduce los 160 px a 96, y min-w-32 tiene la última palabra y sube a 128.
  3. 192 px con box-border, que es el valor de Preflight, y 192 + 48 + 16 = 256 px con box-content.
  4. 32 px en flujo de bloques, porque los márgenes colapsan y gana el mayor. En flex flex-col son 48 px, porque se suman.

Los cuatro resultados son los que se midieron en las secciones 2, 5 y 12.

Ejercicio 2: Alerta de stock

Crea una alerta con fondo ámbar, ancho completo de su contenedor, 24 px de relleno lateral y 16 px vertical. Debe tener un título y un texto separados 4 px: “Stock bajo” y “Quedan 4 unidades de la Torre Workstation.”.

Ver solución
<div class="rounded-lg bg-amber-100 px-6 py-4">
  <h2 class="font-bold">Stock bajo</h2>
  <p class="mt-1 text-sm">Quedan 4 unidades de la Torre Workstation.</p>
</div>

No hace falta w-full: un <div> ya ocupa todo el ancho. px-6 son 24 px, py-4 son 16 px y mt-1 son 4 px.

Ejercicio 3: Bloque centrado y adaptable

Crea una tarjeta “Condiciones de envío” centrada, que no supere los 448 px, y que en un móvil estrecho no toque los bordes de la pantalla.

Ver solución
<section class="bg-gray-100 px-4 py-8">
  <article class="mx-auto max-w-md rounded-lg bg-white p-6">
    <h2 class="font-bold">Condiciones de envío</h2>
    <p class="mt-2 text-sm">Los pedidos se envían en 24 horas.</p>
  </article>
</section>

max-w-md limita a 448 px sin fijar el ancho, y mx-auto centra. El px-4 va en el padre: si lo pusieras como margen en la tarjeta, chocaría con mx-auto, porque ambas clases escriben el mismo margen.

Ejercicio 4: Barra de navegación

Crea una barra con la marca EMPRESA y los enlaces Catálogo y Ofertas a la izquierda, y el enlace Entrar pegado a la derecha. Debe funcionar también en una interfaz RTL y no usar ningún elemento vacío.

Ver solución
<nav class="flex items-center gap-4 bg-gray-100 px-4 py-2">
  <span class="font-bold">EMPRESA</span>
  <a href="#">Catálogo</a>
  <a href="#">Ofertas</a>
  <a href="#" class="ms-auto">Entrar</a>
</nav>

ms-auto se queda con todo el espacio libre antes de “Entrar”. Con ml-auto el diseño se rompería en RTL, porque el espacio libre quedaría en el lado equivocado.

Ejercicio 5: Sustituye space-*

Este menú muestra un espacio de más cuando se ocultan categorías con JavaScript. Corrígelo.

<ul class="space-y-2 text-sm">
  <li class="bg-gray-100 p-2">Portátiles</li>
  <li class="hidden bg-gray-100 p-2">Sobremesa</li>
  <li class="bg-gray-100 p-2">Monitores</li>
  <li class="hidden bg-gray-100 p-2">Periféricos</li>
</ul>
Ver solución
<ul class="flex flex-col gap-2 text-sm">
  <li class="bg-gray-100 p-2">Portátiles</li>
  <li class="hidden bg-gray-100 p-2">Sobremesa</li>
  <li class="bg-gray-100 p-2">Monitores</li>
  <li class="hidden bg-gray-100 p-2">Periféricos</li>
</ul>

En 4.x, space-y-2 pone el margen en todos los hijos menos en el último del HTML, que aquí es uno oculto. Por eso “Monitores” lleva 8 px de más. gap-2 solo cuenta los elementos que se muestran.

Ejercicio 6: Pila de avatares

Crea cuatro avatares circulares de 48 px solapados 12 px, con un borde blanco para separarlos.

Ver solución
<div class="flex -space-x-3">
  <div class="size-12 rounded-full border-2 border-white bg-indigo-600"></div>
  <div class="size-12 rounded-full border-2 border-white bg-orange-500"></div>
  <div class="size-12 rounded-full border-2 border-white bg-teal-500"></div>
  <div class="size-12 rounded-full border-2 border-white bg-gray-400"></div>
</div>

Aquí space-* sí tiene sentido: gap no puede ser negativo. -space-x-3 son -12 px.

Ejercicio 7: Pie al fondo

Crea una página con cabecera de 64 px, contenido centrado que no supere 768 px, y un pie que quede abajo aunque haya poco contenido. La solución debe funcionar en móviles.

Ver solución

Es el patrón de la sección 10:

<body class="flex min-h-dvh flex-col bg-gray-50 text-gray-900">
  <header class="flex h-16 items-center bg-indigo-700 px-6 font-bold text-white">EMPRESA</header>

  <main class="mx-auto w-full max-w-3xl flex-1 px-6 py-8">
    <h1 class="text-2xl font-bold">Tu carrito está vacío</h1>
    <p class="mt-2 text-gray-600">Vuelve al catálogo para añadir productos.</p>
  </main>

  <footer class="bg-gray-200 px-6 py-4 text-sm text-gray-700">© EMPRESA. Pie siempre abajo aunque haya poco contenido.</footer>
</body>

min-h-dvh y no h-dvh: con mucho contenido la página debe poder crecer. min-h-screen también sería válido, pero en móvil obliga a desplazar la página para ver el pie.

Ejercicio 8: Fila de empleado en RTL

Crea una fila con avatar, nombre y un botón “Ver” al final. Debe llevar una barra de color al inicio del texto. Comprueba que se adapta sola al poner dir="rtl".

Ver solución
<section dir="rtl">
  <div class="flex items-center gap-3 border-s-4 border-indigo-600 bg-gray-100 py-2 ps-4 pe-2">
    <div class="size-10 rounded-full bg-indigo-600"></div>
    <p class="text-sm">Isabel Torres</p>
    <button class="ms-auto rounded bg-indigo-600 px-3 py-1 text-sm text-white">Ver</button>
  </div>
</section>

Todo es lógico: border-s-4, ps-4, pe-2 y ms-auto. border-s-* pertenece a Bordes, sombras y efectos. py-2 y px-3 pueden quedar así porque son simétricas.

Ejercicio 9: Traduce a Tailwind 3.4

Un compañero necesita compilar en 3.4 este fragmento. Escribe el equivalente de cada clase.

p-13   w-md   h-lh   pbs-4   inline-56   size-dvh   w-(--ancho)   max-w-3xs   w-7/13
Ver solución
Tailwind 4.3 Tailwind 3.4
p-13 p-[3.25rem]
w-md w-[28rem]
h-lh h-[1lh]
pbs-4 pt-4 en escritura horizontal
inline-56 w-56 en escritura horizontal
size-dvh size-[100dvh]
w-(--ancho) w-[--ancho]
max-w-3xs max-w-[16rem]
w-7/13 w-[calc(7/13*100%)]

Todas se comprobaron compilándolas en 3.4.19. Lo que no te avisa es el compilador: una clase de 4.x en 3.4 simplemente no genera CSS, y el elemento se queda sin estilo.

Ejercicio 10: Panel de aplicación

Crea un panel con una barra lateral fija de 256 px, una cabecera de 56 px y una lista de 30 pedidos, de modo que solo se desplace la lista y la página no tenga barra de desplazamiento propia.

Ver solución
<body class="bg-gray-50 text-gray-900">
  <div class="flex h-dvh">
    <aside class="w-64 shrink-0 space-y-2 bg-indigo-900 p-4 text-white">
      <p class="mb-4 font-bold">Panel EMPRESA</p>
      <a href="#" class="block rounded bg-indigo-700 px-3 py-2">Pedidos</a>
      <a href="#" class="block rounded px-3 py-2">Clientes</a>
    </aside>

    <div class="flex min-w-0 flex-1 flex-col">
      <header class="flex h-14 shrink-0 items-center bg-white px-6 font-bold shadow-sm">Pedidos</header>
      <main class="flex-1 overflow-y-auto p-6">
        <ul class="space-y-3" id="lista"></ul>
      </main>
    </div>
  </div>

  <script>
    const lista = document.getElementById("lista");
    for (let i = 0; i < 30; i++) {
      const li = document.createElement("li");
      li.className = "rounded bg-white p-4 shadow-sm";
      li.textContent = `Pedido ${5001 + i}`;
      lista.append(li);
    }
  </script>
</body>

Medido en una ventana de 700 px: el documento mide 700 px y no se desplaza; main mide 644 px y su contenido, 2076 px.

  • h-dvh fija la altura total.
  • shrink-0 impide que la barra lateral y la cabecera encojan.
  • min-w-0 permite que la columna central sea más estrecha que su contenido.
  • overflow-y-auto (se estudia en Layout y posicionamiento) añade el desplazamiento y, además, permite que main encoja por debajo de su contenido. Por eso aquí no hace falta min-h-0. Haría falta si el desplazamiento estuviese en un envoltorio dentro de main, como viste en la sección 12.

Ejercicio 11 (propuesto): Ficha de producto

Maqueta la ficha del Portátil Gaming 16: imagen a la izquierda y datos a la derecha, con un ancho total limitado a max-w-4xl. Usa gap-* para separar las columnas y max-w-prose para la descripción. Debe verse bien a 400 px y a 1100 px de ancho.

Ejercicio 12 (propuesto): Modo compacto

Toma la tabla o lista que prefieras y añade un modo compacto con [--spacing:0.2rem] en el contenedor. Compruébalo en la tarjeta del catálogo y decide qué valores conviene fijar para que no cambien de tamaño. ¿Cómo lo harías en Tailwind 3.4?


Resumen

Concepto API Idea clave
Modelo de caja box-border, box-content Con Preflight, w-* incluye relleno y borde
Escala --spacing, número × 4 px Multiplicadores libres desde 4.0; lista cerrada en 3.4
Relleno p-*, px-*, pt-*, ps-* Está dentro de la caja y lleva el fondo. No admite negativos
Margen m-*, mx-auto, -mt-* Está fuera; admite auto y negativos; colapsa en bloques
px, py, mx, my padding-inline, padding-block Lógicas desde 4.0; solo se nota en escritura vertical
Separación gap-*, gap-x-*, gap-y-* Requiere flex o grid; la mejor opción
Separación antigua space-x-*, space-y-* Da margen a los hijos; selector distinto en 3.4 y 4.x
Anchura w-64, w-1/2, w-full, w-fit Fracción del padre; w-fit respeta al padre, w-max no
Anchos con nombre w-md, max-w-md w-md desde 4.0; max-w-md en ambas versiones
Altura h-16, h-full, h-lh h-full necesita altura en el padre; h-lh desde 4.2
Viewport h-svh, h-lvh, h-dvh, min-h-dvh En móvil, vh es la altura grande
Cuadrados size-10, size-1/2 Equivale a w-* + h-*; desde 3.4
Límites min-*, max-*, max-w-prose min gana a max, y max gana a width
Desbordes en flex min-w-0, min-h-0 El mínimo automático impide encoger
Lógicas ps-*, pe-*, ms-*, me-* Se adaptan a RTL; no las mezcles con físicas
Lógicas de bloque pbs-*, mbs-*, inline-*, block-* Desde 4.2; en 3.4, usa las físicas
A medida w-[37px], w-(--ancho), --spacing-*, --container-* Corchetes en ambas; paréntesis desde 4.0; no repitas nombres en el tema