Alinear los elementos de una tarjeta, un formulario o una lista de productos sobre una misma grilla, cuando cada bloque contiene un volumen de texto distinto, sigue siendo uno de los problemas recurrentes de la maquetación web. Durante años, la respuesta pasaba por JavaScript de medición, alturas fijas o trucos de flexbox. El valor subgrid de CSS Grid Level 2 resuelve este problema de forma nativa: un contenedor anidado puede ahora heredar las pistas (filas o columnas) definidas por su padre, en lugar de calcular su propia grilla.
El problema que resuelve subgrid
Tomemos una fila de tarjetas de producto: imagen, título, descripción, precio, botón. Si cada tarjeta define su propia grilla interna, nada garantiza que el título de la tarjeta 1 y el de la tarjeta 3 ocupen la misma altura de pista, porque la descripción de la tarjeta 3 ocupa dos líneas más. El resultado visual: botones de compra que dejan de alinearse horizontalmente. Históricamente existían dos soluciones alternativas: fijar una altura arbitraria (que se rompe en cuanto cambia el contenido) o medir las alturas con JavaScript después del renderizado (costoso, y visible como un salto en la maquetación). subgrid elimina la necesidad de ambas: la tarjeta deja de definir sus propias pistas y reutiliza las del padre.
Sintaxis: heredar en lugar de redefinir
La sintaxis se mantiene cercana a la de CSS Grid clásico, con una diferencia clave: en lugar de enumerar tamaños de pista, se escribe subgrid en grid-template-columns o grid-template-rows. El propio elemento debe ser un ítem de grilla que abarque varias pistas del padre para que la herencia tenga sentido.
.tarjetas {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto auto auto 1fr auto;
gap: 1.5rem;
}
.tarjeta {
display: grid;
grid-row: span 5;
grid-template-rows: subgrid;
}
.tarjeta__titulo { grid-row: 2; }
.tarjeta__descripcion { grid-row: 3; }
.tarjeta__precio { grid-row: 4; }
.tarjeta__accion { grid-row: 5; }
Cada .tarjeta abarca las cinco filas del padre gracias a grid-row: span 5, y luego declara grid-template-rows: subgrid para reutilizar exactamente esas cinco filas como su propia grilla interna. Los hijos de cada tarjeta se posicionan después sobre esas filas con números que corresponden a la grilla del padre, no a una grilla local recalculada.
Líneas con nombre: mantener el código legible en una grilla compartida
En cuanto una grilla supera las tres o cuatro pistas, los números de línea en bruto (grid-row: 2, grid-row: 4) se vuelven difíciles de leer, sobre todo cuando varios desarrolladores trabajan sobre los mismos componentes. CSS Grid permite nombrar las pistas, y esos nombres se propagan automáticamente a las grillas hijas en subgrid, lo que evita tener que redeclararlas en cada nivel de anidamiento.
.tarjetas {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows:
[media-inicio] auto
[titulo-inicio] auto
[descripcion-inicio] auto
[precio-inicio] 1fr
[accion-inicio] auto
[accion-fin];
gap: 1.5rem;
}
.tarjeta {
display: grid;
grid-row: media-inicio / accion-fin;
grid-template-rows: subgrid;
}
.tarjeta__titulo { grid-row: titulo-inicio; }
.tarjeta__precio { grid-row: precio-inicio; }
La ventaja no es solo estética: si se inserta una fila en la grilla del padre, los nombres evitan tener que renumerar manualmente todos los componentes hijos que hacen referencia a las pistas siguientes.
Compatibilidad con navegadores
La funcionalidad se considera ya Baseline Widely Available, la referencia para juzgar que una funcionalidad web puede usarse sin reservas en todo el parque de navegadores modernos.
| Navegador | Versión mínima compatible con subgrid |
|---|---|
| Chrome / Edge | 117 |
| Firefox | 71 |
| Safari | 16.0 |
| Opera | 103 |
| Safari iOS | 16.0 y posteriores |
Para proyectos que todavía deben cubrir versiones antiguas de Chrome o Edge (114 a 116, donde la funcionalidad existía pero estaba deshabilitada por defecto), envolver la declaración en una consulta de funcionalidad evita una rotura silenciosa de la maquetación: @supports (grid-template-rows: subgrid) { … }, con un recurso de respaldo en grilla clásica o flexbox en el bloque por defecto.
Casos de uso más allá de las tarjetas de producto
Los componentes de tipo «tablero de tarjetas» (comparativas de precios, fichas de equipo, grillas de artículos) se benefician del mismo tratamiento que el ejemplo inicial: la grilla del padre marca el ritmo, y cada hijo se limita a ocupar las celdas que le corresponden sin recalcular jamás su propia geometría.
Formularios: un segundo caso de uso concreto
Los formularios largos, compuestos por varios grupos de campos (datos de contacto, dirección, pago), sufren el mismo problema que las tarjetas de producto: sin una grilla compartida, nada garantiza que las etiquetas, los campos de entrada y los mensajes de error se alineen en una columna común de un grupo a otro. Al declarar una grilla de columnas a nivel del formulario y dejar que cada fieldset herede esas columnas mediante grid-template-columns: subgrid, la etiqueta «Código postal» y la etiqueta «Número de tarjeta» terminan alineadas en la misma columna, aunque una pertenezca a un grupo de tres campos y la otra a uno de dos. El mismo mecanismo alinea los mensajes de error en una columna dedicada, estén presentes o no en cada campo, sin reservar un espacio vacío antiestético cuando un campo es válido.
Con subgrid, la alineación de una interfaz deja de depender del contenido más largo de una fila: es la grilla del padre la que impone el ritmo, no el texto más extenso.
Errores habituales
Tres errores aparecen con más frecuencia en las revisiones de código. Primero, olvidar que el elemento anidado debe abarcar varias pistas del padre (grid-row: span n o grid-column: span n): sin eso, subgrid no tiene nada que heredar y el elemento se comporta como una grilla de una sola pista. Segundo, mezclar subgrid en un eje con dimensiones explícitas en el otro, algo perfectamente válido pero que confunde a quien lee el código si el caso no trivial no se comenta. Por último, apilar varios niveles de subgrid anidados: el soporte existe, pero cada nivel adicional complica la depuración en las DevTools y dificulta razonar sobre el origen real de una pista.
La ganancia va más allá del confort visual y se confirma en la práctica, sobre todo para los equipos que ya usaban container queries para componentes adaptables, ya que ambas funcionalidades se combinan de forma natural: una gestiona el tamaño del contenedor, la otra la alineación interna. Combinado con design tokens bien estructurados para los espaciados, subgrid permite construir grillas de página totalmente gobernadas por un número reducido de variables, sin duplicar los valores de pista en cada nivel de anidamiento.
Lo que hay que recordar
Subgrid no añade una capacidad espectacular a CSS Grid: elimina una limitación. Las grillas anidadas heredan ahora las pistas de su padre en lugar de recalcularlas, lo que elimina toda una categoría de trucos de alineación para tarjetas, formularios y listas con altura de contenido variable. Con un soporte de navegadores ya amplio, su adopción depende sobre todo de si todavía es necesario mantener un recurso de respaldo para un público residual en versiones antiguas.
Es el tipo de funcionalidad CSS que no hace ruido pero que realmente cambia el día a día: en mis proyectos de cliente, subgrid me ha permitido eliminar varias decenas de líneas de JavaScript de medición de altura que arrastraba desde hacía años en grillas de tarjetas. Recomiendo introducirlo de forma progresiva, componente a componente, en lugar de reescribir toda una grilla de página de golpe: así las regresiones de alineación son mucho más fáciles de detectar. — Simon Janvier
Para profundizar: la especificación y los ejemplos de referencia están detallados en la página de Subgrid en MDN.
