От автора: я долгое время писал статьи для сайта SitePoint, и мне очень нравится их шаблон в виде плиток с дизайнерской точки зрения. Плитка содержит всю необходимую информацию о статье: заголовок, автор, дата, категория и даже количество лайков и комментариев.
Мне пришло в голову, что было бы интересно создать такой компонент как с точки зрения HTML, так и CSS. В этой статье мы создадим плиточный шаблон и пройдемся по каждому этапу, постараемся сделать его как можно лучше со стороны доступности, обслуживания, стилизации и SEO производительности.
Начнем с контента
Компонент почти всегда должен создаваться в следующем порядке: контент, разметка, стили и в конце JS (если нужен). Не будем отходить от этого правила и начнем с контента.
1 2 3 4 5 |
HTML & CSS 8 comments A Tale of CSS and Sass Precision by Hugo Giraudel May 12, 2016 |
Теперь можно заворачивать наш контент в HTML. В качестве главного контейнера выступит тег article, он тут идеально подходит. Внутри у нас будет контейнер для верхней части, контейнер для заголовка (хотя он и необязателен) и футер с метаданными.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<article class="c-article-tile"> <div class="c-article-tile__header"> HTML & CSS 8 comments </div> <div class="c-article-tile__body"> A Tale of CSS and Sass Precision </div> <footer class="c-article-tile__footer"> by Hugo Giraudel May 12, 2016 </footer> </article> |
Обратите внимание: мы используем BEM систему объявлений с пространствами имен для именования классов. Вы же можете использовать, что вам больше нравится. Теперь нам нужны дополнительные контейнеры для наших элементов. Один для категории, другой для счетчика комментариев, подходящий заголовок, контейнер для автора, а также для даты. Не забудем добавить ссылки:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<article class="c-article-tile"> <!-- шапка --> <div class="c-article-tile__header"> <a class="c-article-tile__category" href="//www.sitepoint.com/html-css/"> HTML & CSS </a> <a class="c-article-tile__comment-count" href="//www.sitepoint.com/a-tale-of-css-and-sass-precision/#comments"> 8 comments </a> </div> <!-- основная часть --> <div class="c-article-tile__body"> <h2 class="c-article-tile__title"> <a href="//www.sitepoint.com/a-tale-of-css-and-sass-precision/"> A Tale of CSS and Sass Precision </a> </h2> </div> <!-- футер --> <footer class="c-article-tile__footer"> <span class="c-article-tile__author"> by <a href="//www.sitepoint.com/author/hgiraudel/"> Hugo Giraudel </a> </span> <time class="c-article-tile__date" datetime="2016-05-12T12:00"> May 12, 2016 </time> </footer> </article> |
Смотрится неплохо! Пара интересностей:
мы не используем в верхней части тег header, потому что для хедера свойственен заголовок, которого у нас нет;
мы используем тег span, а не p, так как нам просто нечего заворачивать в абзац;
для даты мы используем подходящий тег time с атрибутом datetime, а не span.
Заменим слово «comments» подходящей доступной иконкой.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<svg style="display: none"> <symbol id="icon-bubble" viewBox="0 0 32 32"> <path class="path1" d="M16 2c8.837 0 16 5.82 16 13s-7.163 13-16 13c-0.849 0-1.682-0.054-2.495-0.158-3.437 3.437-7.539 4.053-11.505 4.144v-0.841c2.142-1.049 4-2.961 4-5.145 0-0.305-0.024-0.604-0.068-0.897-3.619-2.383-5.932-6.024-5.932-10.103 0-7.18 7.163-13 16-13z"></path> </symbol> </svg> <!-- … --> <a class="c-article-tile__comment-count" href="//www.sitepoint.com/a-tale-of-css-and-sass-precision/#comments"> 8 <svg class="icon icon-bubble" aria-label="comments"> <use xlink:href="#icon-bubble"></use> </svg> </a> |
Обратите внимание, как мы используем атрибут aria-label, чтобы сделать иконку доступной для пользователей с ограниченными возможностями. Осталось добавить микроданные в код, чтобы облегчить его сканирование и индексацию поисковыми системами. Загляните на Schema.org в раздел статей.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<article class="c-article-tile" itemscope itemtype="//schema.org/Article"> <!-- хедер --> <div class="c-article-tile__header"> <a class="c-article-tile__category" href="//www.sitepoint.com/html-css/" itemprop="keywords"> HTML & CSS </a> <a class="c-c-article-tile__comment-count" href="//www.sitepoint.com/a-tale-of-css-and-sass-precision/#comments" itemprop="commentCount"> 8 <svg class="icon icon-bubble" aria-label="comments"> <use xlink:href="#icon-bubble"></use> </svg> </a> </div> <!-- основная часть --> <div class="c-article-tile__body"> <h2 class="c-article-tile__title" itemprop="headline"> <a href="//www.sitepoint.com/a-tale-of-css-and-sass-precision/"> A Tale of CSS and Sass Precision </a> </h2> </div> <!-- футер --> <footer class="c-article-tile__footer"> <span class="c-article-tile__author"> by <a href="//www.sitepoint.com/author/hgiraudel/" itemprop="author"> Hugo Giraudel </a> </span> <time class="c-article-tile__date" datetime="2016-05-12T12:00" itemprop="datePublished"> May 12, 2016 </time> </footer> </article> |
Прежде чем мы перейдем к стилям, хотелось сказать пару слов и инкапсуляции и правильной реализации дизайна. Компонент по своему определению должен быть многоразовым. Дабы быть повторно использованным в адаптивном контексте, у компонента не должно быть фиксированных размеров, он должен занимать все пространство контейнера.
Таким образом, сам контейнер задает границы инкапсулированного компонента. В нашем случае в роли контейнера может выступать элемент списка, та его часть, ответственная за отображение плиток (карточек или чего-то другого). Плитки могут выглядеть вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<ul class="c-tile-list"> <li class="c-tile-list__item"> <article class="c-article-tile">…</article> </li> <li class="c-tile-list__item"> <article class="c-article-tile">…</article> </li> <li class="c-tile-list__item"> <article class="c-article-tile">…</article> </li> </ul> |
На данном этапе мы полностью закончили работу с разметкой. Разметка чистая, доступная и дружит с SEO. Больше тут делать нечего, перейдем к стилям!
Пишем стили
В части CSS для всех элементов мы будем придерживаться правильной блоковой модели. Также мы будем много работать с flexbox. Собственно, почему бы и нет?
Компонент контейнер списка
Компонент списка очень тонкий, тут почти нечего стилизовать. Контейнер должен обеспечить сетку для наших плиток, управлять расстоянием между ними, а также следить, чтобы плитки были одной высоты. Благодаря flexbox, это не так уж и сложно.
1 2 3 4 5 6 7 8 9 10 11 |
/** * 1. Сбрасываем стили по умолчанию для списка * 2. Flexbox используется для создания сетки под плитки. */ .c-tile-list { list-style: none; /* 1 */ margin: 0; /* 1 */ padding: 0; /* 1 */ display: flex; /* 2 */ flex-wrap: wrap; /* 2 */ } |
Элементы списка:
1 2 3 4 5 6 7 8 9 10 11 |
/** * 1. С помощью flexbox плитки на одной строке имеют одну высоту. * 2. Проверяем время. * 3. Расстояние между плитками. */ .c-tile-list__item { display: flex; /* 1 */ flex-direction: column; /* 1 */ flex: 0 0 300px; /* 2 */ margin: 10px; /* 3 */ } |
Компонент плитка статьи
Перейдем к самому сложному – компонент плитки статьи. Тут нужно очень много стилей, начнем с самой плитки. Как было сказано ранее, плитка не должна иметь фиксированных размеров и должна полагаться только на размеры своего контейнера. Также мы сделаем плитку флекс контейнером, чтобы выровнять футер в нижней части, не опираясь на высоту самой плитки.
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * 1. Выровняем футер внутри плитки с минимальной высотой. * 2. Сделаем так, чтобы плитка занимала всю высоту родителя, если она находится внутри * флекс контейнера. */ .c-article-tile { display: flex; /* 1 */ flex-direction: column; /* 1 */ flex: 1 0 auto; /* 2 */ border: 1px solid rgba(0, 0, 0, 0.1); background-color: rgb(255, 255, 255); } |
Можно перейти на следующий уровень и стилизовать внутренние контейнеры (хедер, основной контейнер, футер). Всем им необходимо прописать горизонтальный padding, и для упрощения дальнейшего позиционирования необходимо превратить их в флекс контейнеры.
1 2 3 4 5 6 7 |
.c-article-tile__header, .c-article-tile__body, .c-article-tile__footer { display: flex; padding-left: 20px; padding-right: 20px; } |
Контент в хедере и футере немного меньше, так как это метаданные, и они не должны занимать слишком много пространства. Можно совершенно безопасно уменьшить размер шрифта обоих контейнеров.
1 2 3 4 |
.c-article-tile__header, .c-article-tile__footer { font-size: 80%; } |
Мы прописали основные стили для контейнеров. Перейдем к стилизации отдельных элементов. Начнем с хедера. Необходимо добавить вертикальных отступов и символическую нижнюю границу, чтобы блок визуально походил на хедер.
1 2 3 4 5 6 7 8 9 |
/** * 1. Очень удобно опираться на свойство `color` для установки цвета нижней границы * это повышает уровень стилизации темы. */ .c-article-tile__header { padding-top: 15px; padding-bottom: 10px; border-bottom: 2px solid; /* 1 */ } |
Направление по умолчанию для флекс контейнера – row. Именно поэтому мы задаем его для внутренних контейнеров в неявном виде. Есть два способа выровнять счетчик комментариев в хедере по правому краю. Первый способ – задать свойство justify-content: space-between для хедера, чтобы вытолкнуть элементы. Мы воспользуемся вторым способом – задать свойство margin-left: auto на самом счетчике и положиться на самый большой секрет flexbox.
1 2 3 4 5 6 |
/** * 1. Правое выравнивание счетчика комментариев в хедере. */ .c-article-tile__comment-count { margin-left: auto; /* 1 */ } |
С хедером закончили, можно переходить к основному контейнеру и заголовку статьи. Основному блоку нужно добавить вертикальных отступов, а самой плитке типографских стилей.
1 2 3 4 5 6 7 8 9 10 |
.c-article-tile__body { padding-top: 20px; padding-bottom: 20px; } .c-article-tile__title { margin: 0; color: #333; font-size: 150%; } |
Наконец, можно разобраться с футером – самой интересной частью. Во-первых, футер должен быть выровнен по нижнему краю плитки.
Во-вторых, футер должен помещаться на одной строке. Сделать это можно при помощи свойства white-space: nowrap. Однако мы не знаем точную длину имени автора, поэтому нужно проверить, чтобы макет не сломался с очень длинным именем. Я знаю, что «усечение строк не подходит для контен…», но в нашем случае это меньшее из двух зол.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
/** * 1. Нижнее выравнивание футера в плитке. * 2. Делаем так, чтобы контент в футере не перепрыгивал на вторую строку */ .c-article-tile__footer { padding-top: 10px; padding-bottom: 15px; margin-top: auto; /* 1 */ white-space: nowrap; /* 2 */ color: #949494; } /** * 1. Делаем так, чтобы имя автора и дата не перекрывали друг друга в случае, если * они не влезают на одну строку; добавляем к имени многоточие. * 2. Если автор и дата влезают, то визуально это незаметно; но все равно * добавляем отступ между ними, если они приблизятся на одной строке. */ .c-article-tile__author { text-overflow: ellipsis; /* 1 */ overflow: hidden; /* 1 */ margin-right: 5px; /* 2 */ } /** * 1. Контейнер даты с правым выравниванием в футере. */ .c-article-tile__date { margin-left: auto; /* 1 */ } |
По стилям плитки закончили! Осталось обговорить всего одну вещь.
Темы
Вы могли заметить, что про темизацию компонента мы не сказали ни слова. Я считаю, что цветовые схемы должны быть отделены от макета: их цель в другом, поэтому с ними нельзя работать точно так же (и не нужно). В случае с темизацией я люблю использовать безкомпонентные классы с префиксом t-. Добавим класс темы к нашей плитке:
1 2 3 4 5 |
<article class="c-article-tile t-sass" itemscope itemtype="//schema.org/Article"> … </article> |
Теперь на основе этого класса мы можем прописывать стили для нашей плитки. Мы добавим цвет нашей плитке и всем ее ссылкам. Стили эффектно перейдут на нижнюю границу хедера, но не затронут текст футера, у которого он уже есть (#949494).
1 2 3 4 |
.c-article-tile.t-sass, .c-article-tile.t-sass a { color: #c69; } |
Отлично, осталось решить проблему с цветом заголовка. Он розовый, а должен быть черным и только при наведении становиться розовым. Необходимо принудить ссылку унаследовать цвет родителя (#333), когда родитель не находится в состоянии hover или active. Можно по-умному воспользоваться псевдоклассом :not():
1 2 3 4 5 6 7 |
/** * 1. Делаем так, чтобы ссылка наследовала цвет родителя только, когда он не * в состоянии hover/active. */ .c-article-tile__title:not(:hover):not(:active) > a { color: inherit; /* 1 */ } |
Заключение
Вот и все! Статья получилась большой, но, надеюсь, вам понравилось. Думаю, это идеальный пример правильной инкапсуляции компонентов, управления темой, а также работы с flexbox. Поиграйтесь с демо. Если придумаете, как сделать шаблон еще лучше, пишите об этом в комментариях!
Автор: Hugo Giraudel
Источник: //www.sitepoint.com/
Редакция: Команда webformyself.