Sobre subgrid y líneas coloreadas

Publicado:
Última edición:
Tiempo de lectura:
15 min.

Para mí, la subgrid siempre fue lo que me faltaba para integrar realmente grid en mis flujos de trabajo.

Con CSS grid, las “pistas de fila” y “columna” creadas en un grid sólo pueden utilizarse para posicionar a los hijos directos del grid contenedora. Subgrid permite compartir todos los valores definidos en el grid antepasado.

En lugar de definir explícitamente los nombres de las líneas y las funciones de dimensionamiento de las pistas, utilizamos la palabra clave subgrid como valor de grid-template-columns o grid-template-rows para heredar la pista del grid más cercana.

Por ejemplo, un grid clásica de 12 columnas podría ser creada como padre para toda la página, y entonces podemos organizar nuestros elementos dentro de ella, sin importar lo profundamente anidados que estén. Puede consultar la compatibilidad actual del navegador con CSS Subgrid en caniuse.com.

Saltar el índice de contenidos

índice de contenidos

Preparación

Tengo un diseño para un nuevo proyecto que parece hecho para Subgrid: La página está dividida en cuatro columnas dentro de un wrapper. Las cuatro columnas están visiblemente divididas por líneas verticales de 1px de ancho que van desde la parte superior de la página hasta la parte inferior. Todos los elementos y contenidos se alinean directamente con estas líneas, algunos contenidos se reparten en varias columnas, otros sólo empiezan en una línea específica del grid.

Configuración del HTML

He escrito el siguiente HTML para el prototípo:

<body>
  <div class="wrapper">
    <header class="landmark">
      <a href="#" class="logo"> Biscuit! </a>
      <svg><!-- ... --></svg>
    </header>
    <main class="landmark">
      <article class="chocolate">
        <h2>Chocolate cake</h2>
        <p>Cookie tart cake cotton candy chocolate chocolate.</p>
        <section>
          <h3>Jelly beans</h3>
          <p>
            Jelly beans gummi bears halvah halvah croissant lemon drops donut gummi bears
            candy canes. Icing sugar plum chupa chups jelly-o soufflé jelly-o pudding
            lollipop. Chocolate bar muffin bonbon pie tootsie roll danish bear claw
            cheesecake.
          </p>
        </section>
        <section>
          <h3>Cheesecake</h3>
          <p>Powder halvah soufflé caramels soufflé chocolate cake halvah.</p>
        </section>
        <section>
          <!-- ... -->
        </section>
        <section>
          <!-- ... -->
        </section>
      </article>
    </main>
    <footer class="landmark">
      <p>
        Diving into subgrid. Created and maintained by
        <a href="#">Lene</a>
      </p>
      <nav>
        <ul role="list">
          <li>
            <a href="#">RSS Feed</a>
          </li>
          <li>
            <a href="#">Follow</a>
          </li>
        </ul>
      </nav>
      <p>Crafted with semantic HTML.</p>
    </footer>
  </div>
</body>

El sistema de grid principal

Primero, defino las dimensiones para El wrapper.

.wrapper {
  inline-size: clamp(16rem, 93vw, 120rem);
  margin-inline: auto;
  position: relative;
}

Este wrapper es para dar cabida a mi sistema de grid en toda la página. Sin embargo, quiero dejar mi clase envoltorio intacta, sólo debe ocuparse de lo que su nombre implica.

Mi elemento <body> tiene un elemento child directo, mi wrapper. Utilizo este selector para definir el sístema grid por separado (una nueva clase con el nombre correspondiente también sería una opción).

body > div {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

El wrapper se divide ahora en cuatro columnas del mismo tamaño, que se colocan directamente una al lado de la otra sin espacio entre ellas.

the Pen

subgrid con líneas: por defecto

El wrapper tiene tres elementos child, que ahora se colocan cada uno en las tres primeras columnas. Este es su comportamiento natural ya que son elementos child de un grid y se dividen a sí mismos en los carriles disponibles.

Activar subgrid

Quiero que los tres hitos conserven el grid padre para sí mismos.

¡Activemos subgrid!

:is(header, main, footer).landmark {
  display: grid;
  grid-template-columns: subgrid;
}

Esto no funciona todavía. Puedo ver en las herramientas de desarrollo que la subgrid está activo, pero los tres todavía están atrapados en una columna.

Fragmento del DOM de las herramientas de desarrollo de Firefox. Muestra el elemento body del documento y su elemento hijo, un div con clase wrapper. Los tres 'landmarks' header, main y footer están subordinados a éste. El wrapper está etiquetado como grid, los tres 'landmarks' como subgrid.
Captura de pantalla de la pestaña inspector de las herramientas para desarrolladores de Firefox

Esto se debe a que siguen siendo elementos hijos directos del sístema grid padre.

En cambio, quiero que ocupen todo el ancho del wrapper y, por tanto, especificaré exactamente qué columnas del grid principal deben ocupar:

:is(header, main, footer).landmark {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
}

the Pen

subgrid con lineas: subgrid activada

Ahora nuestros tres puntos de referencia se apoderan del grid antecesora y colocan a sus propios hijos en ella. Sólo <main> se extiende por todas las columnas, pero aún así sólo ordena sus elementos en la primera columna. Llegaremos a eso en un momento.

Si echo un vistazo a las herramientas de desarrollo, también puedo ver que se han creado tres filas automáticamente.

Merece la pena echar un vistazo a las herramientas de desarrollo del grid: En Firefox, cuando la página contiene un grid con un subgrid, la entrada de la subgrid aparece sangrada bajo su principal en la sección del grid superpuesta.

Captura de pantalla que muestra las casillas de verificación del grid superpuesta y los ajustes de visualización del grid para hacer visibles las líneas de un subgrid o del grid principal. El grid envolvente está seleccionado.
Captura de pantalla de las herramientas para desarrolladores de Firefox: Panel CSS, vista Diseño, sección grid

Colocación de elementos en el grid

Por defecto, los elementos hijo se colocan en la primera línea disponible del grid

Por ahora no tengo que preocuparme del elemento anchor en el <header>.
Pero de acuerdo con el diseño, el SVG debe alinearse con la cuarta línea del grid. Logro esto alineando generalmente a la última línea de la columna:

header.landmark svg {
  grid-column-start: -1;
}

main

Mientras que <main> es un subgrid, su hijo <article> no lo es: el entorno de flujo por defecto está en su lugar. Por alguna razón tuve la sensación de que debería usar los valores de subgrid con moderación. Pero en realidad no hay ninguna razón para ello. Así que convierto todos los elementos <article> que son hijos directos de <main> en subgrids también.

main.landmark > article {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
}

Mirando el diseño, el título y el párrafo del <article> no deben colocarse en el flujo normal del grid, sino que cada uno ocupa su propia fila dentro de la primera columna. Hago que el párrafo ocupe su propia fila dentro de la misma columna.

main.landmark > article > p {
  grid-row: 2;
}

Las secciones de <main> están distribuidas en diferentes columnas en el diseño del escritorio. Ahora las coloco explícitamente en la fila y columna que se les ha asignado. En nuestro ejemplo, sólo hay secciones dentro de main.landmark, pero teniendo en cuenta la compatibilidad futura, <main> dará cabida a más elementos <article> en un HTML más extendido.

article.work > section:first-of-type {
  grid-column: 2 / -1;
  grid-row: 3;
}

article.work > section:nth-of-type(2) {
  grid-column: 3 / -1;
  grid-row: 4;
}

article.work > section:nth-of-type(3) {
  grid-column: span 1 / -1;
  grid-row: 5;
}

article.work > section:last-of-type {
  grid-column: 3 / -1;
  grid-row: 6;
}

También quiero colocar elementos en su propia fila en el <footer>. Puedo definir esta fila extra directamente en el landmark, y además del subgrid definido anteriormente, obtiene su propio valor grid-template-rows.

Se supone que el primer elemento hijo se extenderá sobre dos columnas y filas, el menú y el último pragrafo se alinearán uno debajo del otro a la derecha.

footer.landmark {
  grid-template-rows: repeat(2, auto);
}

footer.landmark p:first-of-type {
  grid-column: span 2;
  grid-row: 1 / -1;
}

footer.landmark p:last-of-type,
footer.landmark nav {
  grid-column: span 2 / -1;
  place-self: end;
}

repeat(2, auto) significa que habrá dos filas en el grid, y ambas deberán tener la misma altura, determinada dinámicamente en función de su contenido.
auto" indica que la altura de las filas se ajustará automáticamente a su contenido.

the Pen

subgrid con líneas: colocando elementos

Líneas del grid coloreadas

Nuestros elementos están alineados, pasemos a la parte que a priori parece sencilla: las columnas de nuestra maquetación deben estar marcadas por líneas divisorias de colores. ¿No puedo simplemente colorear las líneas del grid?

Mi idea original en realidad era, añadir de alguna manera un color a las líneas del grid de la columna en El wrapper en el fondo.

Eso no funciona. Puedo mostrar las líneas en las herramientas de desarrollo, pero no parece que tenga una manera de abordar realmente estas líneas con un color, usando CSS. Sin embargo, he encontrado algunos enfoques en la naturaleza (En otras palabras: Stack Overflow, también me atreví a preguntar Chat GTP, pero eso fue completamente inútil). Los enfoques no fueron creados con subgrid en mente, pero intentarlo no hace daño.

Idea 1: Añadir un hueco y un color de fondo del grid principal

Intentemos añadir un gap de 1px y un background-color pardel grid principal.

body > div {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background-color: blue;
  gap: 1px;
}

A continuación, se asigna a las subgrids el color de fondo predeterminado.

:is(header, main, footer).landmark,
main.landmark > article {
  display: grid;
  grid-template-columns: subgrid;
  grid-column: 1 / -1;
  background-color: white;
}

the Pen

subgrid con lineas: gap y color de fondo

Bueno, eso sólo funcionó a medias. Las filas creadas intrínsecamente del grid principal son de color azul, pero como los contenedores de la subgrid abarcan todas las columnas, los huecos ya no son visibles.

Idea 2: border-color

¿Puedo asignar un borde a todos los elementos del grid?

body > div > * {
  border-inline-start: 1px solid blue;
}

body > div > *:last-of-type {
  border-inline-end: 1px solid blue;
}

Ahora mi grid principal tiene un borde a la derecha y a la izquierda, porque son las zonas que no están superpuestas.

¿Y si repito esto para las subgrids?

:is(header, main, footer).landmark > *,
main.landmark > article > * {
  border-inline-start: 1px solid blue;
}

:is(header, main, footer).landmark > *:last-of-type,
main.landmark > article > *:last-of-type {
  border-inline-end: 1px solid blue;
}

Ahora tengo un caos de líneas verticales, dobladas a los lados, porque el borde sólo se aplica donde hay elementos.

the Pen

subgrid con lineas: borde

Idea 3: background-image: linear-gradient

Con este planteamiento, me despido del intento de dirigirme directamente del grid.

Recordé que podría colocar un patrón CSS repetido en el fondo, ya que las cuatro columnas son totalmente uniformes. Esto sería como una capa gráfica que sólo intenta replicar las columnas de mi grid.

Tras un poco de ensayo y error, me decidí por esta variante:

body > div {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  background-image: linear-gradient(to right, blue 1px, transparent 1px);
  background-size: 24.95%;
  background-repeat: repeat;
}

Eso es establecer un gradiente lineal para la propiedad background-image, creando un patrón repetitivo de una línea azul de 1 píxel sobre un fondo transparente, y cubriendo el 24,95% del ancho del contenedor. Perdón por el número aleatorio (también llamado “número mágico”, como he aprendido de Christopher Kirk-Nielsen).

the Pen

subgrid con lineas: linear-gradient

Es algo “hacky” y ligeramente fuera, pero lo suficientemente bueno por ahora. Estoy bastante seguro de que todavía voy a cambiarlo, ya que no produce un buen resultado en todos los viewport.

Esta no es una solución satisfactoria - Espero que alguien va a venir con una idea mejor y hágamelo saber acerca de él 😬

Es bastante “hacky” y muestra una clara desviación del grid en los dispositivos móviles. Estoy bastante segura de que todavía voy a cambiarlo, ya que no produce un resultado agradable en cada viewport.
De hecho, espero que a alguien se le ocurra una idea mejor y me la comunique 😬.

Actualización: Ideas de la comunidad CSS

Compartí el artículo en Mastodon y, como esperaba, gente con talento aportó nuevas ideas! 🎉

linear-gradient sin número mágico

Christopher Kirk-Nielsen se encargó de mi problema con los números mágicos, transformando mi solución en una mucho más limpia:

body > div {
  --cols: 4;
  --line-size: 1px;
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  background-image: linear-gradient(to right, blue var(--line-size), transparent 0);
  background-size: calc((100% - var(--line-size)) / var(--cols));
}

Puso el número de columnas y el line-width en variables y calculó el ancho deseado a partir de ahí. Lo bueno es que ahora hay una relación entre eg grid y el patrón, y podemos ajustar dinámicamente el grosor de la línea (--line-size), y el número de columnas (--cols) - se aplicarán tanto para el sistema de grid subyacente como para las líneas visibles.

the Pen

subgrid con líneas: linear-gradient (sin número mágico)

Eso es fantástico, gracias! 💚

Pseudoelementos

Roma Komarov ha añadido la idea de crear elementos vacíos adicionales como marcadores de posición y pseudoelementos como líneas de grid.

Roma aplica pseudoelementos CSS antes de los hijos del elemento <article> y después del propio elemento <article>.

article > *::before,
article::after {
  contenido: '';
  position: absolute;
  /* Omitir intencionadamente el inset-inline, utilizando la posición "inicial" de estos elementos */
  inset-block: 0;
  anchura: 1px;
  color de fondo: azul;
  z-index: -1;
}

article::after {
  derecha: 0;
}

Roma observa que este método genera más líneas de las necesarias: cuando hay elementos que empiezan en las mismas líneas del grid, se solapan, lo que se hace visible al añadir un valor de opacity:

article > *::before,
article::after {
  /* todas las demás declaraciones CSS */
  opacidad: 0.1;
}

the Pen

subgrid con líneas: pseudo-elementos

Me encanta esta idea, ¡gracias! 💚

Roma recomienda colocar elementos vacíos en el grid, ya que cualquier cambio en las posiciones actuales de los hijos de <article> en el grid puede hacer desaparecer las líneas. Vamos a hacer esto en la siguiente sección.

Marcadores de posición implementados, vista móvil

Tomé la idea de Roma e implementé los marcadores de posición en otro codepen, y también añadí un media query para la mayoría de los elementos colocados en la rejilla, para hacerlos abarcar todo el ancho en dispositivos móviles (y conseguir que las líneas “funcionen” también en viewports pequeños).

No quiero aburrir a nadie y no voy a repetir todos los ajustes CSS aquí, si quieres saber los detalles, echa un vistazo al Codepen:

the Pen

subgrid con lineas: pseudo-elementos, placeholders y media query

Actualización 2: El enfoque de Josh Comeau

Unos días después, Josh Comeau publicó uno de sus elaborados artículos, An Interactive Guide to CSS Grid. Allí no entra en subgrid, pero contribuye casualmente a la solución de mi problema.

Para ilustrar mejor sus ejemplos, enfatiza las líneas invisibles del grid. Su enfoque es similar al de Roma: En el contexto de los pseudoelementos no trabaja con background-color, sino con border-left (Estoy utilizando propiedades lógicas en su lugar, border-inline-end o border-right funcionaría de la misma manera para nosotros).

Su solución tiene un caso de uso diferente, así que pude simplificar su versión.
Apliqué esto al último ejemplo con los marcadores de posición vacíos (mantuve las líneas discontinuas de Josh para hacer más visible la diferencia con la solución anterior):

div.placeholder > *::before,
div.placeholder::after {
  content: '';
  position: absolute;
  inset-block: 0;
  border-inline-start: 2px dashed blue;
}

div.placeholder::after {
  right: 0;
}

the Pen

subgrid with lines: Josh Comeau's pseudo-element borders

Más sobre subgrid

  1. Clásico: mdn
  2. Rachel Andrew con grandes ejemplos en su artículo para 12 Days of Web, edición 2022
  3. Artículo en web.dev, basado en la idea de un grid “macro” a nivel de página
  4. Se pueden encontrar grandes ejemplos de subgrid en gridbyexample.com
  5. “Learn CSS Subgrid” por Ahmad Shadeed
  6. Michelle Barker introduce subgrid en su “Creative CSS Layout” en CSS Day 2022 (YouTube)
  7. … y un libro: CSS - The Definitive Guide

Mi compañero se rió cuando me vio sentado delante del ordenador con un libro técnico, de la vieja escuela. En realidad, aquí es donde primero miro cuando quiero aprender o saber algo sobre CSS. Hay más de 80 páginas sólo sobre diseño de cuadrícula, y lo recomiendo encarecidamente. Por supuesto, no es barato y ocupa una cantidad notablemente grande de espacio en la estantería.

El libro CSS - The Definitive Guide yace abierto en mi escritorio en un capítulo sobre subgrid, con mi teclado detrás.

Intento mantener mis artículos actualizados y, por supuesto, podría equivocarme o podría haber una solución mejor. Si ves algo que (ya) no es correcto, o algo que debería mencionarse, no dudes en editar el artículo en GitHub.

Webmentions

¿Has publicado una respuesta? Dime dónde: