Як створити адаптивні таблиці з чистим CSS за допомогою модуля розмітки сітки

TL; DR

Найпопулярніший спосіб відображення колекції подібних даних - використання таблиць, але таблиці HTML мають недолік, оскільки їх важко зробити адаптивними.

У цій статті я використовую модуль макета CSS Grid та властивості CSS (і не Javascript) для розміщення таблиць, які обертають стовпці залежно від ширини екрану, що надалі змінюється на картку на основі макета для малих екранів.

Для нетерплячих, подивіться на наступну ручку для прототипу реалізації.

Трохи історії чуйних HTML-таблиць

Адаптивні таблиці - не нова тема, і вже запропоновано багато рішень. “Оперативна таблиця даних”, що вперше була опублікована у 2012 році Крісом Койєром, містить дуже акуратні резюме (включаючи оновлення 2018 року).

“Дійсно чуйні таблиці з використанням CSS3 Flexbox” Васана Субраманіана демонструє ідею загортання стовпців, реалізовану за допомогою Flexbox.

Незважаючи на те, що було запропоновано багато цікавих ідей, такі бібліотеки, як bootstrap, обирають горизонтальну прокрутку для маленьких екранів.

Оскільки зараз у нас CSS Grid, я думаю, що у нас може бути краща загальна альтернатива горизонтальній прокрутці.

Таблиці HTML

Починаючи з основ, таблиця в HTML - це формат макета для відображення колекцій елементів через матрицю рядків і стовпців. Елементи розташовані в рядки з однаковими атрибутами даних в одних стовпцях, причому рядки часто сортуються за одним або кількома сортуваними атрибутами. Формат дає вам можливість з висоти пташиного польоту швидко зрозуміти та вивчити велику кількість даних.

Наприклад, ось гіпотетична таблиця деталей замовлення на придбання, яку ви можете побачити у заявці на придбання.

Елемент, в даному випадку, є деталлю замовлення на придбання, яка має такі атрибути, як номер деталі, опис деталі тощо.

При використанні таблиць HTML макет даних жорстко кодується як рядки та стовпці (наприклад, і ). Цього може бути достатньо для використання екраном, який відповідає всій ширині таблиці, але насправді це не стосується безлічі пристроїв, які існують сьогодні. Що стосується хаків, ви можете змінити властивість відображення таблиць і використовувати будь-який макет, який ви можете зробити з CSS загалом, але це не видається семантично правильним.

Перевизначені таблиці (= колекція предметів)

Почнемо з переосмислення того, як дані таблиці повинні виражатися в HTML.

Як зазначалося раніше, оскільки дані таблиці - це, по суті, впорядкована колекція предметів, представляється природним використовувати упорядковані списки. Крім того, оскільки таблиці часто використовуються для доповнення текстових описів, видається природним включити це в розділ, але це залежатиме від контексту того, як використовуються дані таблиці.

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

Vanilla використовуються для вираження атрибутів елементів, оскільки HTML5 не визначає відповідний тег для цього. Ключовим тут є вираз семантично подібних атрибутів як ієрархії s. Ця структура буде використана при визначенні способу викладання даних. До цього я повернусь у наступному розділі на тему укладання.

Що стосується фактичних даних всередині елемента, то першим елементом у списку є заголовок, а решта елементів - фактичними даними.

Тепер прийшов час розпочати розмову про стилізацію елементів за допомогою CSS Grid.

Стилізація колекцій предметів

Основна ідея тут полягає у тому, щоб відображати всі атрибути елемента як звичайну таблицю, якщо дозволяється ширина відображення. Цей макет має розкіш можливості бачити якомога більше предметів (рядків).

Коли ширина дисплея звужується, деякі атрибути складаються вертикально, щоб заощадити горизонтальний простір. Вибір атрибутів укладання повинен базуватися на:

  1. Чи мають сенс атрибути, якщо їх складати вертикально? і,
  2. При вертикальному укладанні це заощаджує горизонтальний простір?

When the width further shrinks to the size of a mobile device, each item is displayed as a card. This layout has redundancy because the attribute names are repeatedly displayed on each card, and has the least glanceability, but does not compromise usability (e.g. horizontal scrolling, super small text, etc).

Now let’s dive into the details.

Styling Step 1: Full Table

Here’s a visual summary of how things will be implemented with CSS Grid.

In order to make columns wrap, multiple grid containers are defined as a hierarchy. The red box is a grid container for each row, and the blue box is a container for each column group that wraps.

Let’ s start by setting the list as a grid container by defining a class called .item-container and applying it to the

  • (the red box).

    .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

  • Original text


  • .

    .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.