wandres.dev
FLEXBOX: EL ALGORITMO · Base, crecer, encoger

min-width: auto, la causa número uno de 'no se encoge'

El tamaño mínimo automático de los elementos flex y grid: qué es, por qué la especificación lo introdujo, cómo se calcula exactamente y las cuatro formas de desactivarlo con criterio.

⏱ 22 min

Un elemento flex con flex: 1 y un texto largo dentro se niega a encogerse por debajo de cierto punto, desborda su contenedor y ensancha la página. overflow: hidden no lo arregla, text-overflow: ellipsis no aparece, y flex-shrink parece no funcionar. No es un bug: es el tamaño mínimo automático, una regla que la especificación de flexbox introdujo a propósito en 2012 y que se lleva por delante a todo el que no la conoce. Es, con diferencia, el comportamiento de CSS que más horas de depuración inútil ha generado.

🎯 Al terminar esta lección sabrás
  • Explicar qué es el tamaño mínimo automático y por qué existe.
  • Enunciar la condición exacta que lo activa y la que lo desactiva.
  • Reconocer los cuatro síntomas clásicos y asociarlos con esta causa.
  • Aplicar la corrección correcta en flex y su equivalente en grid.

El valor inicial que cambió

En CSS 2.1, el valor inicial de min-width y min-height era 0. La especificación de flexbox lo cambió a auto, y definió que en un elemento flex ese auto no significa cero: significa un mínimo basado en el contenido.

La motivación está escrita en la propia especificación y es razonable. Sin ese mínimo, flex-shrink podría reducir un elemento hasta cero píxeles, y su contenido —un texto, una imagen, un botón— desaparecería por completo de la pantalla. Un layout que hace desaparecer contenido al estrecharse es peor que uno que desborda, porque el desbordamiento se ve y la desaparición no. La especificación eligió proteger el contenido.

El resultado es que, por defecto, un elemento flex nunca se encoge por debajo del tamaño mínimo de su contenido en el eje principal. Si su contenido tiene un min-content de 400px porque incluye una palabra larguísima o una imagen de ancho fijo, el elemento no bajará de 400px por mucho que le pidas.

⚠️
No es solo flexbox

Los elementos grid tienen la misma regla, con un matiz adicional: el mínimo automático se aplica solo si el elemento ocupa alguna pista cuya función mínima de dimensionado sea auto. Como 1fr es la forma corta de minmax(auto, 1fr), la condición se cumple casi siempre, y de ahí sale el conocido minmax(0, 1fr).

La condición exacta

La regla completa, tal y como está en CSS Flexbox 1, sección 4.5, es esta: el valor auto de la propiedad de tamaño mínimo del eje principal especifica un tamaño mínimo automático si el overflow del elemento en ese eje es visible. En cualquier otro caso, computa a 0.

Esa segunda mitad es la clave operativa, y explica un montón de recetas que circulan sin justificación. overflow: hidden “arregla” el problema no porque recorte nada, sino porque al no ser visible hace que min-width: auto compute a cero. Lo mismo con overflow: auto, scroll y clip.

El tamaño mínimo automático se calcula como el menor de tres candidatos, cuando aplican:

La sugerencia de tamaño especificada: el valor de inline-size del elemento si es una longitud definida. Un elemento con inline-size: 200px no tendrá un mínimo mayor que 200px.

La sugerencia de tamaño transferida: para elementos con relación de aspecto y un tamaño definido en el eje transversal, el tamaño que resulta de aplicar la proporción.

La sugerencia de tamaño del contenido: el min-content del elemento en el eje principal, que es el que manda en la mayoría de los casos reales.

Es decir: si el elemento tiene un tamaño especificado, ese acota el mínimo automático; si no lo tiene, manda el min-content del contenido.

Los cuatro síntomas

Estos cuatro casos son la misma causa vista desde cuatro sitios. Reconocerlos ahorra horas.

El texto que no hace puntos suspensivos. Quieres truncar un título largo dentro de un elemento flex y el truncado no aparece nunca; en su lugar, el elemento se ensancha.

<div class="fila">
  <span class="titulo">Un título francamente muy largo que debería truncarse</span>
  <button class="accion">Abrir</button>
</div>
.fila { display: flex; gap: 1rem; inline-size: 20rem; }
.titulo {
  flex: 1;
  overflow: hidden;         /* esto también desactiva el mínimo automático */
  text-overflow: ellipsis;
  white-space: nowrap;
}

Aquí el arreglo llega de rebote: overflow: hidden era necesario para text-overflow de todos modos, y como efecto secundario pone el mínimo a cero. Por eso este caso a veces “se arregla solo” y a veces no, según el orden en el que pruebes las cosas.

El bloque de código que ensancha la página. Un pre dentro de un elemento flex tiene un min-content igual a su línea más larga, porque el contenido preformateado no se parte. Ese mínimo sube por el árbol y ensancha el contenedor, el layout y la página.

.panel { flex: 1; min-inline-size: 0; }   /* la corrección */
.panel pre { overflow-x: auto; }          /* y el scroll donde toca */

La imagen que no encoge. Una imagen tiene tamaño intrínseco, y ese tamaño se convierte en el min-content del elemento que la contiene. Sin max-inline-size: 100% en la imagen, el elemento flex hereda un mínimo del tamaño real del archivo.

La barra lateral que desborda en móvil. Dos paneles con flex: 1 que en escritorio caben y en pantalla estrecha desbordan. Cada panel se niega a bajar de su min-content, la suma supera el ancho de la ventana, y aparece scroll horizontal en toda la página.

Las correcciones, en flex y en grid

No hay una única respuesta correcta; hay cuatro, y cada una es la buena en un contexto distinto.

/* 1. min-inline-size: 0 — desactiva el mínimo, deja el contenido desbordar
      visiblemente. La opción por defecto para paneles que ya gestionan
      su propio desbordamiento por dentro. */
.panel { flex: 1; min-inline-size: 0; }

/* 2. overflow: hidden o auto — desactiva el mínimo y además recorta o
      hace scroll. La opción cuando quieres truncar o desplazar. */
.panel { flex: 1; overflow: auto; }

/* 3. min-inline-size con un valor tuyo — sustituye el mínimo del motor
      por uno que tú controlas. La opción cuando el elemento tiene un
      tamaño por debajo del cual deja de ser usable. */
.buscador { flex: 1; min-inline-size: 8rem; }

/* 4. Arreglar el contenido — a veces el mínimo es enorme porque el
      contenido no se puede partir, y eso sí es un problema real. */
.panel { flex: 1; }
.panel { overflow-wrap: break-word; }

La cuarta merece énfasis. Si tu elemento tiene un min-content de 600px, la pregunta correcta no siempre es cómo ignorarlo: a veces es por qué el contenido no se puede partir. Una URL sin overflow-wrap, una imagen sin tope, una tabla sin scroll. Desactivar el mínimo en esos casos esconde el síntoma y deja el desbordamiento visible.

💡
El reset defensivo y su precio

Muchos equipos añaden .flex > * { min-inline-size: 0; } como regla general. Funciona y elimina una clase entera de bugs, pero renuncia a la protección que la especificación quería darte: a partir de ahí, un elemento puede encogerse hasta cero y su contenido desaparecer sin aviso. Si lo adoptas, hazlo sabiendo qué estás desactivando.

En grid el problema tiene el mismo origen y una quinta corrección propia, porque allí el mínimo se puede fijar en la pista en lugar de en el elemento.

/* Se desborda: 1fr equivale a minmax(auto, 1fr) y el auto es el mínimo
   de contenido de la pista */
.rejilla { display: grid; grid-template-columns: 1fr 1fr; }

/* No se desborda: el mínimo de la pista es cero */
.rejilla { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }

Arreglarlo en la pista es mejor que arreglarlo en el elemento cuando la rejilla es tuya, porque la regla queda en un solo sitio y no depende de qué elementos acaben dentro. Arreglarlo en el elemento es mejor cuando escribes un componente que no sabe dónde va a vivir.

El mínimo automático es la especificación eligiendo el fallo menos malo, y por eso duele

Lo que hace tan desconcertante a esta regla no es su complejidad, que es escasa, sino que sea la única de todo el layout de CSS que prioriza el contenido por encima de tu declaración. Le has dicho al elemento que encoja, tiene flex-shrink: 1, y se niega. En cualquier otra parte del lenguaje, una declaración explícita gana; aquí no, porque el comité tomó una decisión de producto: entre desbordar visiblemente y hacer desaparecer contenido, desbordar es un fallo recuperable y desaparecer no lo es. Un desbordamiento se ve, se reporta y se arregla; un texto encogido a cero píxeles no lo detecta nadie hasta que un usuario se queja de que falta información. La especificación eligió el fallo ruidoso sobre el fallo silencioso, y eso es casi siempre lo correcto en un sistema que no puede lanzar errores. El coste de esa decisión lo pagas tú cada vez que te encuentras con un layout que no encoge, y es un coste alto porque el mecanismo es invisible: no hay ninguna declaración en tu CSS que explique el comportamiento, el valor culpable es un auto que nunca escribiste y cuyo significado cambió por debajo de ti al añadir display: flex a un ancestro. La conclusión que conviene interiorizar es más amplia que flexbox: cuando un sistema declarativo desobedece tu declaración, casi nunca es un bug, es una restricción de nivel superior que alguien consideró más importante que tu instrucción. Buscar cuál es esa restricción, en vez de insistir con la instrucción, es la diferencia entre depurar y dar palos de ciego.

⚔️ Provoca el bug y desmóntalo
  1. Monta la fila con título largo y botón, sin ninguna corrección, y confirma que el título desborda.
  2. Añade min-inline-size: 0 y observa el cambio. Quítalo y añade overflow: hidden. Explica por qué ambos funcionan.
  3. Mide en DevTools el min-content del elemento culpable y comprueba que coincide con el ancho al que se detiene.
  4. Reproduce el caso con un pre de línea larga y arréglalo sin usar min-inline-size.
  5. Traduce el mismo layout a grid y arréglalo con minmax(0, 1fr).
  6. Explica por qué flex-shrink: 999 no soluciona ninguno de los casos anteriores.