Cómo crear tablas receptivas con CSS puro usando Grid Layout Module

TL; DR

La forma más popular de mostrar una colección de datos similares es usar tablas, pero las tablas HTML tienen el inconveniente de ser difíciles de hacer receptivas.

En este artículo, utilizo el módulo de diseño de cuadrícula CSS y las propiedades de CSS (y no Javascript) para diseñar tablas que envuelven columnas según el ancho de la pantalla, lo que cambia aún más a una tarjeta basada en el diseño para pantallas pequeñas.

Para los impacientes, mire el siguiente bolígrafo para una implementación prototípica.

Un poco de historia de las tablas HTML receptivas

Las tablas receptivas no son un tema nuevo y hay muchas soluciones que ya se han propuesto. El "Resumen de datos de tablas de respuesta", publicado por primera vez en 2012 por Chris Coyier, tiene las cosas muy bien resumidas (incluida una actualización de 2018).

“Really Responsive Tables using CSS3 Flexbox” de Vasan Subramanian muestra una idea de envolver columnas, implementada con Flexbox.

Aunque se han propuesto muchas ideas interesantes, bibliotecas como bootstrap optan por el desplazamiento horizontal para pantallas pequeñas.

Como ahora tenemos CSS Grid, creo que podríamos tener una mejor alternativa común al desplazamiento horizontal.

Tablas HTML

Comenzando con lo básico, una tabla en HTML es un formato de diseño para mostrar colecciones de elementos a través de una matriz de filas y columnas. Los elementos se disponen en filas, con los mismos atributos de datos en las mismas columnas, con las filas a menudo ordenadas con uno o más atributos clasificables. El formato le ofrece una vista panorámica para captar y examinar rápidamente grandes cantidades de datos.

Por ejemplo, aquí hay una tabla hipotética de detalles de órdenes de compra, que puede ver en una aplicación de compras.

Un artículo, en este caso, es un detalle de orden de compra, que tiene atributos como número de pieza, descripción de la pieza, etc.

Cuando se utilizan tablas HTML, el diseño de los datos se codifica en filas y columnas (por ejemplo, y ). Esto puede ser suficiente para el uso de una pantalla que se ajuste a todo el ancho de la mesa, pero en realidad, esto no se aplica a la gran cantidad de dispositivos que existen en la actualidad. En términos de trucos, puede alterar la propiedad de visualización de las tablas y usar cualquier diseño que pueda hacer con CSS en general, pero eso no parece semánticamente correcto.

Tablas redefinidas (= colección de elementos)

Comencemos por redefinir cómo se deben expresar los datos de la tabla en HTML.

Como se indicó anteriormente, dado que los datos de la tabla son esencialmente una colección ordenada de elementos, parece natural usar listas ordenadas. Además, dado que las tablas se utilizan a menudo para complementar las descripciones textuales, parece natural incluir esto en una sección, pero esto dependería del contexto de cómo se utilizan los datos de la tabla.


    
  1. # Part Number Part Description ...
  2. 1 100-10001 Description of part ...
  3. ...

Los de vainilla se utilizan para expresar atributos de elementos ya que HTML5 no define una etiqueta adecuada para esto. La clave aquí es expresar atributos semánticamente similares como una jerarquía de . Esta estructura se utilizará para definir cómo deben distribuirse los datos. Volveré a esto en la siguiente sección sobre el tema del estilo.

En cuanto a los datos reales dentro del elemento, el primer elemento de la lista es el encabezado y el resto de los elementos son los datos reales.

Ahora es el momento de empezar a hablar sobre el estilo de los elementos con CSS Grid.

Colecciones de artículos de estilo

La idea básica aquí es mostrar todos los atributos del artículo como una tabla normal, si el ancho de la pantalla lo permite. Este diseño tiene el lujo de poder ver tantos elementos (filas) como sea posible.

Cuando el ancho de la pantalla se reduce, algunos atributos se apilan verticalmente para ahorrar espacio horizontal. La elección de los atributos de apilamiento debe basarse en:

  1. ¿Los atributos tienen sentido cuando se apilan verticalmente? y,
  2. Cuando se apila verticalmente, ¿ahorra espacio horizontal?

Cuando el ancho se reduce aún más al tamaño de un dispositivo móvil, cada elemento se muestra como una tarjeta. Este diseño tiene redundancia porque los nombres de los atributos se muestran repetidamente en cada tarjeta y tiene la menor visibilidad, pero no compromete la usabilidad (por ejemplo, desplazamiento horizontal, texto superpequeño, etc.).

Ahora profundicemos en los detalles.

Paso de estilo 1: mesa completa

Aquí hay un resumen visual de cómo se implementarán las cosas con CSS Grid.

Para hacer que las columnas se envuelvan, se definen varios contenedores de cuadrícula como una jerarquía. El cuadro rojo es un contenedor de cuadrícula para cada fila y el cuadro azul es un contenedor para cada grupo de columnas que envuelve.

Comencemos configurando la lista como un contenedor de cuadrícula definiendo una clase llamada .item-containery aplicándola a la

  • (el cuadro rojo).

    .item-container { display: grid; grid-template-columns: 2em 2em 10fr 2fr 2fr 2fr 2fr 5em 5em; }

    The number of explicit columns specified with grid-template-columns is nine, which is the number of top-level 's, directly  under

  • .

    The column’s width is defined in relative length to make the columns wrap. The actual fraction has to be fine-tuned, based on the content.

    The columns that don’t wrap are defined in absolute length to maximize width usage for the wrapping columns. In the purchase order details example, the second column is a two-digit Id, so I set the width to double that size of 2 m’s.

    Next, we define another grid container called .attribute-container and apply it on all intermediate ’s under the list (the blue box).

    .attribute-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(var(--column-width-min), 1fr)); }

    The minimum column width for all grid items under .attribute-container is specified with a CSS variable called --column-width-min(more on this later) using the minmax function, with the maximum set to take the rest of the space (e.g. one fraction). Since grid-template-columns are repeated, available horizontal space will be split into the maximum number of columns that could take at least --column-width-min, and the rest of the columns would go to the next line. The column’s width will be stretched if there is excess horizontal space because the repeat is auto-fited.

    Styling Step 2: Wrapping Table

    Next, --column-width-min needs to be specified independently for each column in order to wrap. Just to be clear, the variables need to be specified in order for the full table to render properly as well. To do this, a class is set for each .attribute-container, and a different --column-width-min is specified for each class scope.

    Let’s take a look at the HTML where .part-id is applied,

     Part Number Part Description 

    and the CSS:

    .part-id { --column-width-min: 10em; }

    This specific grid container will have two columns, as long as the available width is wider than 10em for each grid item (e.g. the grid container is wider than 20em). Once the grid container’s width becomes narrower than 20em, the second grid item will go to the next row.

    When we combine CSS properties like this, we need only one grid container .attribute-container, with the details changing where the class is applied.

    We can further nest .attribute-containers, to have multiple levels of wrapping with different widths, as in the following exert.

     Part Number Part Description Vendor Number Vendor Name .part-information { --column-width-min: 10em; } .part-id { --column-width-min: 10em; } .vendor-information { --column-width-min: 8em; }

    All of the above is enclosed in the following media query. The actual breakpoint should be selected based on the width necessary when your table is wrapped to the extreme.

    @media screen and (min-width: 737px) { ... }

    Styling Step Three: Card Layout

    The card layout will look like a typical form with attribute names in the first column and attribute values in the second column.

    To do this, a class called .attribute is defined and applied to all leaf tags under the

  • .

    .attribute { display: grid; grid-template-columns: minmax(9em, 30%) 1fr; }

    The attribute names are taken from a custom attribute of the leaf   called data-name, for example , and a pseudo-element is created. The pseudo-element will be subject to the grid container’s layout.

    .attribute::before { content: attr(data-name); }

    The first item in the list is the header and does not need to be displayed.

    /* Don't display the first item, since it is used to display the header for tabular layouts*/ .collection-container>li:first-child { display: none; }

    And finally, the cards are laid out in one column for mobile devices, but two for screens with a little bit more width, but not enough for displaying a table.

    /* 2 Column Card Layout */ @media screen and (max-width: 736px) { .collection-container { display: grid; grid-template-columns: 1fr 1fr; grid-gap: 20px; } ... } /* 1 Column Card Layout */ @media screen and (max-width:580px) { .collection-container { display: grid; grid-template-columns: 1fr; } }

    Finishing Notes

    Accessibility is an area that wasn’t considered at all and may have some space for improvement.

    If you have any ideas or second thoughts, please feel free to comment!

    And of course, thanks for reading.