Grid Workshop

Subgrid alignment

Demo de CSS subgrid: los títulos, metadatos y footers de las cards quedan alineados en filas comunes aunque el texto tenga alturas distintas. Activá o desactivá el subgrid para comparar.

Subgrid Con subgrid
Portada de The Shadow #1

The Shadow

Guión: W. Gibson
Arte: A. Reyes

Portada de The Shadow #2

The Shadow: la caída del último bastión del norte

Guión: W. Gibson
Arte: A. Reyes
Color: L. Pardo

Portada de The Shadow #3

The Shadow

Guión: W. Gibson
Arte: A. Reyes

Cómo funciona y cómo implementarlo

Cómo funciona

Grid padre — .cards-grid es un grid con filas explícitas; cada card es un grid item que ocupa su celda.
Subgrid anidado — cada .sub-card hace display:grid; grid-template-rows: subgrid; y grid-row: span N, delegando sus filas en las del padre. Así títulos, metadatos y footers comparten las mismas líneas.
Sin subgrid — al quitar subgrid, cada card crece por su propio contenido y los footers quedan a alturas distintas.
Toggle — initSubgrid() alterna la clase no-subgrid en el grid y actualiza el badge y el aria-pressed.

Implementación

1. Marcado — cada card debe tener las mismas secciones en el mismo orden: portada · título · meta · footer.
2. CSS — .cards-grid { display:grid; grid-template-columns:repeat(3,1fr); grid-template-rows: repeat(N, auto); } y .sub-card { display:grid; grid-template-rows:subgrid; grid-row:span N; }
3. Anclar el footer — el .sub-card__footer se coloca en la última fila del subgrid; al delegar en el padre, queda alineado con los demás.
4. Fallback — si el navegador no soporta subgrid, la card vuelve a su grid propio (grid-template-rows normal) y solo pierde la alineación compartida.

Código para replicar

Snippets autocontenidos: copiá cada bloque y pegá en tu proyecto. Las cards deben compartir las mismas secciones en el mismo orden para que el subgrid las alinee.

HTML
<div class="cards-grid">
  <article class="sub-card">
    <div class="sub-card__cover">
      <img src="001.jpg" alt="Portada">
    </div>
    <div class="sub-card__text">
      <h3>The Shadow</h3>
      <p>Guion: W. Gibson<br>Arte: A. Reyes</p>
    </div>
    <div class="sub-card__footer">
      <span class="sub-card__price">$12.990</span>
    </div>
  </article>
  <!-- cards con distinta altura de texto… -->
</div>
CSS
:root{
  --surface:hsl(215,11%,3%);
  --border:hsl(214,35%,22%);
  --text:hsl(215,25%,85%);
}

.cards-grid{
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:12px;
  align-items:start;
}

.sub-card{
  display:grid;
  grid-row:span 4;
  grid-template-rows:subgrid;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:0.625rem;
  overflow:hidden;
}

.sub-card__footer{
  margin-top:12px;
  padding-top:10px;
  border-top:1px solid var(--border);
}

/* Fallback sin subgrid */
.cards-grid.no-subgrid{
  align-items:stretch;
}

.cards-grid.no-subgrid .sub-card{
  display:flex;
  flex-direction:column;
  grid-row:unset;
  grid-template-rows:unset;
}

.cards-grid.no-subgrid .sub-card__footer{
  margin-top:auto;
}
JS
const grid = document.querySelector('.cards-grid');
const btn = document.querySelector('.subgrid-toggle');
const badge = document.querySelector('.subgrid-badge');

btn.addEventListener('click', () => {
  const on = btn.getAttribute('aria-pressed') === 'true';
  btn.setAttribute('aria-pressed', String(!on));
  grid.classList.toggle('no-subgrid', on);
  badge.textContent = on ? 'Sin subgrid — flex column' : 'Con subgrid';
});