La sintaxis de subgrid
El valor subgrid en grid-template-rows y grid-template-columns, la obligación de abarcar pistas, qué pasa cuando abarcas más de las que hay, y por qué cada eje se decide por separado.
Subgrid tiene una sintaxis de una palabra y tres reglas que hay que tener claras: solo funciona en un elemento que ya sea grid, solo afecta al eje en el que lo declares, y el elemento tiene que abarcar explícitamente las pistas que quiere heredar. Fuera de eso no hay nada más que aprender, lo cual dice bastante de lo bien resuelta que quedó la funcionalidad.
- Declarar subgrid en un eje, en los dos, y saber qué cambia.
- Explicar por qué el elemento debe abarcar pistas con
spano con líneas. - Predecir qué ocurre si abarca más pistas de las que existen.
- Distinguir el sistema de coordenadas del subgrid del de su padre.
La declaración
subgrid es un valor de grid-template-rows y de grid-template-columns. Lo escribes en el elemento hijo, que debe ser a su vez un contenedor grid:
.tarjetas {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto; /* tres filas compartidas */
gap: 1.5rem;
}
.tarjeta {
grid-row: span 3; /* abarca las tres filas del padre */
display: grid; /* es un grid... */
grid-template-rows: subgrid; /* ...que hereda las filas del padre */
}
Con esas cuatro líneas, los hijos de .tarjeta se colocan en las mismas pistas de fila que definió .tarjetas. El título de las tres tarjetas ocupa la misma pista, así que las tres tienen la misma altura: la del título más alto. Lo mismo con la descripción y con el enlace.
Las dos condiciones que se suelen olvidar son estas.
El elemento debe ser grid. grid-template-rows: subgrid en un elemento sin display: grid no hace nada. Y display: inline-grid también vale.
El elemento debe abarcar pistas. Un elemento que ocupa una sola fila no tiene nada que heredar: hereda una pista, que es la suya. Necesitas grid-row: span 3 o una colocación explícita entre líneas.
Si tu tarjeta tiene cuatro hijos y declaras grid-row: span 3, el cuarto hijo cae en una pista implícita del subgrid, que no está compartida con las hermanas y por tanto no se alinea. El número del span debe coincidir con el número de pistas que el contenido va a ocupar.
Un eje, o los dos
Cada eje se decide por separado y son independientes. Puedes heredar solo las filas, solo las columnas, o los dos:
/* Solo filas: las columnas internas son propias */
.a { display: grid; grid-template-rows: subgrid; grid-template-columns: auto 1fr; }
/* Solo columnas */
.b { display: grid; grid-template-columns: subgrid; }
/* Los dos */
.c { display: grid; grid-template: subgrid / subgrid; }
El caso de las tarjetas es de filas: quieres que los elementos internos se alineen verticalmente entre tarjetas, y cada tarjeta tiene su propia disposición horizontal.
El caso de un formulario es de columnas: quieres que las etiquetas y los campos se alineen horizontalmente entre filas, y cada fila tiene su propia altura.
El caso de los dos ejes a la vez es menos frecuente y aparece en tablas de datos construidas con divs o en calendarios.
El eje que no declares como subgrid se comporta con total normalidad: puedes definir pistas, usar fr, repeat, lo que quieras. No hay ninguna interferencia entre los dos.
Cuando el subgrid abarca más pistas de las que hay
Si el subgrid abarca más pistas de las que su padre le ofrece, las pistas que faltan se generan como implícitas dentro del subgrid. Esas pistas no existen en el padre, así que no están compartidas con los hermanos y no se alinean.
.tarjetas { grid-template-rows: auto auto; } /* solo dos filas */
.tarjeta { grid-row: span 2; display: grid; grid-template-rows: subgrid; }
/* Si la tarjeta tiene tres hijos, el tercero cae en una pista
implícita del subgrid y no se alinea con nada */
El síntoma es reconocible: dos de los tres elementos se alinean perfectamente y el último no. Cuando lo veas, cuenta las pistas.
También ocurre al revés: si el subgrid abarca menos pistas de las que hay, simplemente hereda las que abarca. No pasa nada raro, solo que las pistas que no abarcas no participan.
El sistema de coordenadas del subgrid
Dentro de un subgrid, las líneas se renumeran empezando por 1 en la primera pista que el subgrid abarca. Esto es importante y es fácil de olvidar.
.tarjetas { display: grid; grid-template-rows: repeat(6, auto); }
.tarjeta {
grid-row: 3 / span 3; /* ocupa las filas 3, 4 y 5 del padre */
display: grid;
grid-template-rows: subgrid;
}
/* Dentro de la tarjeta, esas filas son la 1, la 2 y la 3 */
.tarjeta > .titulo { grid-row: 1; }
Es decir: el subgrid ve un sistema de coordenadas local que empieza donde empieza él. Los números que uses dentro no son los del padre. Los índices negativos también funcionan y se refieren al final del subgrid, no al del padre.
Esa renumeración es lo que hace que un componente con subgrid sea reutilizable: puedes colocarlo en cualquier posición de la rejilla padre y su CSS interno no cambia.
/* Este componente funciona en cualquier posición de su padre */
.fila-datos {
display: grid;
grid-template-columns: subgrid;
grid-column: span 4;
}
.fila-datos > .nombre { grid-column: 1; }
.fila-datos > .estado { grid-column: 2; }
.fila-datos > .fecha { grid-column: 3; }
.fila-datos > .acciones{ grid-column: 4; }
Un subgrid puede tener dentro otro subgrid, y las pistas se propagan por toda la cadena. Un subgrid de un subgrid hereda, en última instancia, las pistas del grid original que las definió. No hay límite de profundidad, aunque en la práctica más de dos niveles suele indicar que el marcado tiene un nivel de más.
Fíjate en la forma que tomó la solución, porque no era la única posible. El grupo de trabajo podría haber añadido una propiedad para “alinear con este otro elemento”, o un mecanismo de grupos de alineación, o algo parecido a las guías de las herramientas de diseño. Eligieron algo más restrictivo y mucho más potente: un valor que dice que este contenedor no define pistas propias, sino que adopta las de su padre. Es herencia, en el sentido literal que la palabra tiene en CSS, aplicada a algo que hasta entonces no se heredaba. Y la elección importa porque determina las propiedades del sistema resultante. Al ser herencia por el árbol, un subgrid solo puede alinearse con lo que está dentro de su ancestro, nunca con un elemento arbitrario de la página: la relación es siempre estructural y localizada, lo que hace que el resultado sea razonable de calcular y de depurar. Una API de “alinéate con aquel” habría creado un grafo de dependencias arbitrario entre elementos lejanos, con ciclos posibles, coste de cálculo impredecible y un modelo mental imposible de mantener en la cabeza. La lección de diseño que se extrae es contraintuitiva y vale para cualquier sistema que construyas: la restricción es lo que hace usable a la funcionalidad. Subgrid puede menos de lo que muchos pedían, y precisamente por eso se puede razonar sobre él y por eso cabe en una lección de diecisiete minutos.