wandres.dev
GRID I · El sistema de dos dimensiones

Cómo se dimensionan las pistas

El algoritmo de dimensionado de pistas en cinco fases, la pareja base y límite de crecimiento, el papel exacto de los gaps y qué resetean en silencio las abreviaturas grid-template y grid.

⏱ 19 min

Grid resuelve el tamaño de sus pistas con un algoritmo de cinco fases que trabaja sobre dos números por pista: una base que solo crece y un límite que la frena. Es más complejo que el bucle de flexbox porque una pista es compartida por todas las filas y su tamaño tiene que ser un compromiso entre contenidos que ni se conocen. Conocer las fases explica por qué el reparto no siempre coincide con la intuición y por qué el orden de las declaraciones no influye pero el de las fases sí.

🎯 Al terminar esta lección sabrás
  • Explicar la pareja base y límite de crecimiento de una pista.
  • Enumerar las cinco fases del algoritmo y qué hace cada una.
  • Situar los gaps en el cálculo y saber contra qué se resuelve un gap en porcentaje.
  • Enumerar qué propiedades resetean grid-template y grid.

Base y límite de crecimiento

Cada pista arranca con dos valores derivados de su función de dimensionado. La base es su tamaño actual y solo crece durante el algoritmo. El límite de crecimiento es el techo hasta el que puede crecer.

Para una pista minmax(min, max), la base sale de min y el límite de max. Un min fijo da una base con ese valor; un min intrínseco da una base de cero que las fases posteriores irán subiendo. Un max fijo da un límite con ese valor; un max intrínseco o flexible da un límite infinito de partida.

pista base inicial límite inicial
200px 200 200
minmax(100px, 300px) 100 300
auto 0, luego el mínimo del contenido infinito
min-content 0, luego el min-content el min-content
1fr 0, luego el mínimo del contenido infinito

La fila de 1fr recuerda lo que ya viste: como es minmax(auto, 1fr), su base no es cero al final del proceso, sino el mínimo del contenido. De ahí que dos pistas 1fr puedan salir desiguales.

Las cinco fases

Fase 1: inicializar. Se asigna a cada pista su base y su límite según la tabla anterior, sin mirar todavía el contenido.

Fase 2: resolver tamaños intrínsecos. Aquí entra el contenido. Se procesan primero los elementos que ocupan una sola pista, subiendo la base de esa pista hasta acomodar su contribución. Después, los que ocupan varias pistas, en orden creciente de número de pistas ocupadas, repartiendo su contribución entre las pistas que abarcan. Y por último los que ocupan alguna pista flexible.

Ese orden es importante y explica un comportamiento que desconcierta: un elemento que abarca dos columnas no ensancha ninguna de las dos si entre las dos ya cabe. Solo cuando no cabe se reparte el exceso, y se reparte entre las pistas que puedan crecer.

Fase 3: maximizar pistas. Si sobra espacio, se reparte por igual entre las bases de todas las pistas hasta que cada una alcance su límite de crecimiento. Las que llegan a su límite dejan de recibir y su parte se redistribuye entre las demás.

Fase 4: expandir las pistas flexibles. Se calcula el tamaño de un fr de forma que las pistas flexibles llenen el espacio restante, y se les asigna. Si el resultado fuese menor que su base, se conserva la base: una pista 1fr nunca es más pequeña que su contenido mínimo.

Fase 5: estirar las pistas auto. Si queda espacio libre y justify-content o align-content valen normal o stretch —que son los valores por defecto— ese espacio se reparte a partes iguales entre las pistas cuyo máximo sea auto.

Esa quinta fase es la que explica por qué una pista auto se estira y una max-content no, aunque en el resto del algoritmo se comporten casi igual.

📝
Por qué el orden de las declaraciones no importa

Las cinco fases se ejecutan siempre en el mismo orden sobre la definición completa de la rejilla. Da igual si escribes grid-template-columns antes o después de gap, o si un elemento aparece antes o después en el HTML: el algoritmo recibe todos los datos a la vez. Lo que sí importa es el orden dentro de la fase 2, que va por número de pistas ocupadas.

Los gaps

gap es la abreviatura de row-gap y column-gap. En grid define un espacio entre pistas adyacentes que se comporta, a efectos del algoritmo, como una pista fija más: se resta del espacio disponible antes de repartir nada.

.rejilla {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.5rem;          /* filas y columnas */
}
.otra {
  gap: 2rem 1rem;       /* row-gap 2rem, column-gap 1rem */
}

Tres detalles que conviene tener claros.

Un gap en porcentaje se resuelve contra el tamaño de la caja de contenido del contenedor en el eje correspondiente: column-gap: 5% mide contra el ancho, row-gap: 5% contra el alto. Si el alto no es determinable, se resuelve contra cero, con lo que el gap desaparece. Es una fuente de sorpresas y una buena razón para usar rem en los gaps.

No hay gap antes de la primera pista ni después de la última. gap es espacio entre, y esa es exactamente la razón de que exista: era lo único que los márgenes no podían expresar sin compensaciones.

Y los gaps no se colapsan nunca, ni entre sí ni con márgenes. Un elemento con margin-inline-end: 1rem en una rejilla con gap: 1rem deja 2rem de separación.

Las abreviaturas y lo que resetean

grid-template es la abreviatura de grid-template-rows, grid-template-columns y grid-template-areas. Su sintaxis con barra es directa:

/* filas / columnas */
.panel { grid-template: auto 1fr auto / 16rem 1fr; }

Como toda abreviatura, resetea lo que no mencionas: si tenías grid-template-areas definido y escribes un grid-template sin áreas, las áreas desaparecen.

grid es una abreviatura mayor y bastante más peligrosa. Además de las tres anteriores, resetea grid-auto-rows, grid-auto-columns y grid-auto-flow a sus valores iniciales.

/* Esto funciona */
.a { grid-auto-rows: 10rem; grid-template-columns: repeat(3, 1fr); }

/* Y esto borra el grid-auto-rows sin decir nada */
.b { grid-auto-rows: 10rem; grid: none / repeat(3, 1fr); }

La recomendación práctica es sencilla: usa grid-template para la definición explícita y las propiedades individuales para las implícitas. La abreviatura grid tiene una sintaxis compacta para definir a la vez las pistas explícitas y el flujo automático, pero es tan densa que casi nadie la lee bien en una revisión de código.

💡
La forma de grid-template con áreas es la que sí merece la pena
.pagina {
  display: grid;
  grid-template:
    "cabecera cabecera" auto
    "lateral  principal" 1fr
    "pie      pie" auto
    / 16rem 1fr;
}

Cada fila lleva su tamaño al final de la línea y las columnas van tras la barra. Es la definición de rejilla más legible que permite la sintaxis, y verás las áreas en detalle en el nivel siguiente.

Grid paga en complejidad de algoritmo lo que te ahorra en complejidad de código

Comparar las cinco fases de grid con el bucle de una sola pasada de flexbox deja ver el intercambio que hay debajo. Flexbox puede resolver cada línea por separado porque ninguna decisión de una línea afecta a otra, y esa independencia es lo que le permite ser barato y ser intrínseco a la vez. Grid no tiene esa suerte: una pista de columna la comparten todas las filas, así que su tamaño depende del contenido de elementos que están en sitios distintos y que quizá abarcan números de pistas distintos. Resolver eso exige las cinco fases, exige procesar los elementos en orden de amplitud para que un elemento ancho no se coma el reparto de uno estrecho, y exige recorrer el contenido varias veces. Es medible: una rejilla con muchos elementos de tamaño intrínseco es uno de los layouts más caros que puedes escribir, y en listas largas la diferencia con una rejilla de pistas fijas se nota en el perfilador. Pero el intercambio es casi siempre bueno, y conviene entender por qué: ese coste de cálculo sustituye a un trabajo que antes hacías tú, en JavaScript, midiendo elementos y escribiendo estilos, con un resultado peor porque tu medición ocurría después del primer pintado. La regla general que se deriva de aquí y que vale para todo el CSS moderno es que cuando la plataforma te ofrece un algoritmo caro, compáralo con lo que costaba hacerlo a mano, no con cero. Casi siempre el motor lo hace en la fase correcta del pipeline y tú no podías.