La trampa de flex: 1
Las cuatro abreviaturas de flex y qué expande cada una, por qué flex 1 no es lo mismo que flex-grow 1, y cómo un número suelto y una longitud suelta significan cosas opuestas.
flex: 1 es la declaración más escrita de todo flexbox y una de las peor entendidas. No es una forma corta de flex-grow: 1: cambia además el tamaño base a cero, y ese cambio es el que decide si tus columnas salen iguales o proporcionales al contenido. Con las abreviaturas de flex pasa lo mismo que con background o font: reinician propiedades que no mencionaste, y ahí es donde se pierden las tardes.
- Expandir de memoria las cuatro abreviaturas de
flexy los valores nombrados. - Explicar la diferencia observable entre
flex: 1yflex-grow: 1. - Distinguir el significado de un número suelto y de una longitud suelta.
- Elegir la abreviatura adecuada para cada uno de los cuatro patrones habituales.
Las expansiones
flex es una abreviatura de tres propiedades y tiene reglas de expansión que no son intuitivas, porque los valores omitidos no toman su valor inicial: toman valores específicos de la abreviatura.
| escribes | flex-grow | flex-shrink | flex-basis |
|---|---|---|---|
flex: 1 |
1 | 1 | 0% |
flex: 2 |
2 | 1 | 0% |
flex: 20rem |
1 | 1 | 20rem |
flex: 1 20rem |
1 | 1 | 20rem |
flex: 2 3 |
2 | 3 | 0% |
flex: initial |
0 | 1 | auto |
flex: auto |
1 | 1 | auto |
flex: none |
0 | 0 | auto |
flex: 0 auto |
0 | 1 | auto |
| no escribes nada | 0 | 1 | auto |
La fila crítica es la primera. flex: 1 pone la base a 0%. Si en lugar de eso escribes flex-grow: 1 a secas, la base se queda en su valor inicial, que es auto.
/* Base cero: las tres columnas acaban idénticas */
.a > * { flex: 1; }
/* Base auto: las tres columnas parten de su contenido */
.b > * { flex-grow: 1; }
Con contenidos de longitudes distintas, el primer bloque da tres columnas exactamente iguales y el segundo da tres columnas desiguales que solo se reparten el sobrante. Son layouts distintos escritos con dos declaraciones que parecen sinónimas.
flex: 1 pone flex-shrink: 1, que ya era el valor inicial, así que ahí no hay sorpresa. Pero flex: none pone flex-shrink: 0, y flex: 0 0 200px también. Si escribes flex-basis: 200px después de un flex: none, tendrás un elemento con base 200 que ni crece ni encoge, que probablemente no era la intención.
Un número no es una longitud
La regla de desambiguación de la abreviatura es la fuente de un error silencioso: un valor sin unidad se interpreta como flex-grow, y un valor con unidad como flex-basis.
.a { flex: 1; } /* grow 1, base 0% */
.b { flex: 1px; } /* grow 1, base 1px */
.c { flex: 0; } /* grow 0, base 0% */
.d { flex: 0px; } /* grow 1, base 0px */
Compara .c y .d. Un solo carácter de diferencia y comportamientos opuestos: .c no crece en absoluto, .d crece con factor 1. Es un error tipográfico que no produce ningún aviso y cuyo síntoma aparece a metros de distancia en el layout.
La regla mnemotécnica: si lleva unidad, hablas de tamaño; si no la lleva, hablas de proporción. Y el 0% de la expansión de flex: 1 es un porcentaje precisamente porque necesitaba una unidad para no ser ambiguo consigo mismo.
Los cuatro nombres
Los valores nombrados existen porque cubren los cuatro patrones que se repiten. Merece la pena usarlos por lo que comunican.
flex: initial es lo que tienes sin escribir nada: el elemento no crece, encoge si hace falta, y parte de su tamaño natural. Es el comportamiento de un elemento que solo quiere estar ahí.
flex: auto es initial más la capacidad de crecer. Reparte el sobrante entre los elementos en proporción a nada —todos con factor 1— pero cada uno conserva su tamaño de partida. Es el valor para menús donde cada opción debe ocupar al menos su texto.
flex: none es rigidez total: ni crece ni encoge, y parte de su tamaño natural. Iconos, avatares, botones con texto que no debe partirse.
flex: 1 es la rejilla: base cero, todos iguales. Columnas de un panel, celdas de una fila de estadísticas, paneles de igual peso.
/* Cuatro elementos, cuatro comportamientos */
.icono { flex: none; } /* nunca se toca */
.etiqueta { flex: initial; } /* cede si hace falta */
.opcion { flex: auto; } /* crece desde su contenido */
.panel { flex: 1; } /* una fracción exacta */
Cuándo escribir las tres propiedades
Hay dos situaciones en las que la abreviatura estorba y conviene ser explícito.
La primera es cuando quieres una base concreta con un comportamiento asimétrico. Un panel que crece libremente pero no encoge por debajo de su base se escribe mejor sin ambigüedad:
.panel { flex-grow: 1; flex-shrink: 0; flex-basis: 18rem; }
/* o, equivalentemente y más corto */
.panel { flex: 1 0 18rem; }
La segunda es cuando la base tiene que venir del contenido pero quieres crecimiento proporcional. Ahí flex: 2 no sirve porque anula la base:
/* Este panel crece el doble que sus hermanos, pero parte de su contenido */
.destacado { flex-grow: 2; flex-shrink: 1; flex-basis: auto; }
/* forma corta */
.destacado { flex: 2 1 auto; }
La regla práctica: si vas a tocar la base, escribe las tres partes. La abreviatura de un solo valor está bien para flex: 1 y flex: none, que son declaraciones de intención reconocibles; en cuanto aparece un tercer número, ser explícito ahorra más tiempo del que cuesta.
La lección que trasciende a flexbox es que en CSS una abreviatura nunca es azúcar sintáctico: es una declaración simultánea de todas sus subpropiedades, incluidas las que no escribiste, que se ponen a su valor inicial o a un valor especial de la abreviatura. background: red borra tu background-image. font: 16px sans-serif borra tu font-weight y tu line-height. flex: 1 borra tu flex-basis y lo sustituye por cero. La diferencia es que en las dos primeras el efecto es visible al instante y en la tercera es sutil: el layout sigue funcionando, solo que reparte el espacio con un criterio que no elegiste. Y hay una consecuencia de arquitectura que casi nadie deduce a tiempo: las abreviaturas se llevan fatal con los sistemas de diseño por capas. Si una utilidad de tu librería pone flex: 1 y otra pone flex-basis: 20rem, el resultado depende del orden en la cascada, no del orden en que las escribiste en el marcado, y el bug es intermitente entre páginas. La regla defensiva que acaban adoptando todos los equipos que mantienen CSS a escala es la misma: escribe abreviaturas en el código de componente y propiedades individuales en el código de utilidad. Y cuando algo no obedece, lo primero que hay que buscar no es la declaración que falta, sino la abreviatura que la borró.