Хабр, привет, это снова я! Меня зовут Антон, я дизайнер b2b продуктов в X5 Tech. Мне нравится моя работа и я стараюсь проектировать реализуемые интерфейсы, поэтому постоянно закапываюсь в технические нюансы.
Какое‑то время назад я писал статью про загрузочные экраны и там коротко рассматривал скелетоны, но делал это не настолько подробно, насколько хотелось бы. Тема богатая, сложная и простая одновременно, надеюсь, понравится. Запасайтесь любопытством, а я, в свою очередь, поделюсь опытом в проектировании скелетонов.
Введение
Мы живём в таком мире, что никто не застрахован от долгой загрузки страниц. И это может коснуться любого продукта, даже с самыми лучшими разработчиками, самым мощным серверным оборудованием и самым быстрым интернетом. Всегда есть подводные камни, вроде опроса сторонних ресурсов или оборудования, которые нельзя собрать заранее, нестабильного интернета у пользователей и многих других неприятностей. Поэтому мы вынуждены спасаться всякими хитростями, вроде экранов загрузки. И в этой статье хочу поделиться, как я думаю, самым гибким и универсальным решением — скелетонами.
Ещё нам необходимо синхронизироваться: пишу я, опираясь на современные JS-решения, хотя и для проектов-с-кучей-легаси это тоже может быть актуально.
И что ещё важно: скелетоны — это не только про визуальную составляющую, но и про техническую реализацию загрузки экранов. То есть, можно сверстать или нарисовать заглушку с серыми прямоугольниками, но она останется заглушкой, а не скелетоном.
Что такое скелетоны
Если есть желание подробнее разобраться в теме загрузочных экранов — добро пожаловать в мою предыдущую статью.
Если нет — постараюсь коротко объяснить суть скелетонов. Вы наверняка видели загружающиеся экраны, которые сначала состоят из маленьких серых прямоугольников и кругов, а потом вместо них появляется контент. Это и есть скелетон. Технически, это тот же экран загрузки, только их много, для отдельных блоков.
Важно уточнить, что скелетоны важно анимировать, без анимации любой статичный и недоступный для пользователя загрузочный экран может восприниматься пользователем как поломка. У скелетонов прижились два типа анимации: пульсация (мерцание) и волна (смещение градиента, лучше делать анимацию единой для всех косточек, так называемый background: fixed).

Плюсы скелетонов в том, что пользователь видит структуру экрана во время его загрузки, давая представление о том, что сейчас загружается. А грамотная техническая реализация скелетона позволяет открывать контент пользователю по мере его загрузки, полноценно открывая доступ к функционалу. То есть не нужно ждать загрузки всей страницы, чтобы воспользоваться панелью навигации, например. Или же можно показать пользователю часть важной информации, пока выполняется сложный запрос.
Скелетоны, структура и разработка
Технически, компонент скелетона — это компонент-обертка для контента и, по совместительству, заглушка на время загрузки. То есть, это всего лишь косточка одного большого скелета загружающегося экрана.
Максимально просто описать принцип скелетирования можно на примере атомарного дизайна. Этот подход, насколько разобрался, впервые был описан в статье за авторством Брэда Фроста.
Согласно статье, на самом низком уровне находятся атомы — самые базовые компоненты библиотеки (текстовые блоки, инпуты, иконки, кнопки и прочее). Далее следует уровень молекул, когда несколько атомов объединяются во что‑то более крупное, после чего из молекул разработчики собирают организмы → шаблоны → страницы. И если смотреть на эту иерархию, процесс скелетирования обычно проходит где‑то между атомами и молекулами или молекулами и организмами.

Но бывают и более интересные случаи — компоненты-обёртки, например, ячейки таблицы. Внутрь них можно положить что угодно, а после обернуть это что угодно компонентом скелетона.

То есть принцип проектирования скелетонов выглядит довольно просто — взяли атомы или молекулы в готовом дизайне, заменили их косточками и всё. Но есть нюансы.
Итак, нюансы
Запросы на сервер
Важной технической частью проектирования скелетонов является продумывание запросов данных с сервера. Можно получать данные не одним пакетом, а несколькими, параллельными или последовательными. То есть возможно запросить сначала обвязку страницы, а затем только данные таблицы. Или запросить сразу всё.

Обратите внимание на изображение выше: можно отдельно забирать шапку, навигацию, панель инструментов, шапку таблицы и первые 20 строк таблицы. Таким образом, пользователь сначала увидит скелет страницы, а далее, по мере загрузки отдельных запросов, контент будет проявляться блоками.
Но тут очень важно понимать: чем больше запросов на сервер, тем больше на него нагрузка, поэтому отдельно запрашивать каждую строчку таблицы будет перебором.
Масштаб скелетирования
Скелетировать можно всю страницу или только отдельные её части, чтобы не тратить бюджет проекта на ненужную работу. На своём опыте я выделяю три подхода к скелетированию.
Полное скелетирование
Подразумевает замену всех или подавляющего большинства экранных элементов косточками. Подход оптимален для высоконагруженных продуктов, когда есть большая вероятность повышенной нагрузки на сервер или когда есть значительная доля пользователей с нестабильным интернетом, которых нельзя игнорировать.

Частичное скелетирование
Выборочное скелетирование больших «тяжёлых блоков», вроде таблицы, карточек, блоков со статистикой и графиками. Тут стоит отметить, что скелетоны прекрасно показывают себя и в виртуализированных списках, и при ленивой загрузке в тех же списках.

Точечное скелетирование
Иногда бывает, что те или иные данные являются плодом множества запросов, опроса целого зоопарка оборудования или сложных вычислений, поэтому отобразить их моментально невозможно, в то время как остальная часть страницы загружается моментально. В таких ситуациях достаточно скелетировать именно такие блоки, чтобы хоть как‑то улучшить пользовательский опыт.

Консистентность
При проектировании загрузки стремитесь к максимальной консистентности интерфейса, ограничивайтесь минимумом графики, балансируя между сложностью реализации и цельностью интерфейса. Если где‑то уже используете скелетоны — спиннеры в выпадающих списках будут выглядеть странно. Дорого делать скелетоны и в выпадающих списках — заглушки можно стилизовать под скелетоны. Не превращайте интерфейс в выставку загрузочных экранов и заглушек.

Точность
Скелетоны должны соответствовать подменяемому контенту, иначе при подгрузке контента блоки на экране будут скачать.
Если срезаете углы использованием заглушек, то следите, чтобы заглушки были свёрстаны согласно реальному контенту, это вводит пользователей в замешательство.

А теперь подробнее
Постараюсь разобрать частные примеры поподробнее, для тех, кому эта статья нравится ?
Основы
Сборка дизайна скелетона начинается с замены компонентов-атомов компонентами-косточками. При этом следует обращать внимание на некоторые параметры и особенности контента.
Обязательно учитывайте тип контента, так как для текста обычно делают уникальные компоненты, когда оптический размер один, а «физический» другой.

Многие пользователи негативно реагируют на скачки контента на странице во время загрузки, поэтому важно учитывать размеры подменяемых компонентов. В первую очередь, это касается высоты.
И нельзя забывать про дизайн-мелочи, поэтому следите за стилистическими особенностями, например, за наличием скруглений у блоков.
База
Как было описано выше, процесс довольно прост: один или несколько компонентов заменяются косточкой-скелетоном. Внутри компонентов косточки не размещаем без одобрения разработчиков.

В примере слева икона лежит не сама по себе, а внутри кнопки-иконки большего размера, поэтому оптические размеры иконки и заменяющей её косточки разные.
В правом примере, для упрощения, селект и кнопка с иконкой заменены одной косточкой.
Формы, декор и усреднение. Или работа с шаблонными блоками
В дизайне могут встречаться шаблонные блоки вроде карточек. Иногда разработчикам может быть удобнее сделать заглушку для них вместо поштучного оборачивания всей мелочи. В таких ситуациях нужно проанализировать все возможные вариации контента и максимально его усреднить, чтобы один шаблон покрыл все вариации карточки.

Разберём пример выше:
тут есть круглый аватар, форму которого легко повторить;
текстовые блоки с именем инициатора изменений и действием можно объединить в один блок, так как он воспринимается единым текстовым блоком, и так будет чуть чище (ага, атом + атом);
у детализации действия может быть разный формат, поэтому есть смысл объединить их в одну косточку (а тут уже 3 атома);
время и дату можно оставить раздельными, сохранив декоративную черту (атом, реальный контент и снова атом).
Результат узнаваемый и охватывает конфигурации контента — профит.
Скелетоны внутри компонентов
Как писал выше, бывают компоненты-обёртки, вроде табличных ячеек. И отсюда вырастает важный нюанс: ячейки обычно уже имеют свои внутренние отступы вокруг контента, поэтому важно правильно подбирать тип скелетонов и их высоту.

Особо внимательные заметят, что косточки занимают не всю ширину ячеек, этот нюанс обязательно опишу ниже.
Но не увлекайтесь рисованием косточек внутри компонентов. Например, запихнуть компонент скелетона в селект потребует больших усилий от разработчиков.
И не размещайте косточку в косточке — это или странно, или вообще никто не увидит этого изыска.

Все эти нюансы можно уточнить у разработчиков. Но приведённые примеры обычно так и работают.
Сложные компоненты и детализация
В дизайне могут встречаться и довольно сложные для скелетирования объекты, поэтому нужно разобраться, а действительно ли нужно стараться передать все формы контента.
Важно понимать, что высокая детализация — это дополнительные трудозатраты, которые не всегда оправданы, и важно принимать взвешенное решение в каждом из случаев.
Снижение детализации
Отличный пример уместного снижения детализации — вкладки. Их сложно разделить на отдельные кнопки-косточки, а разместить скелетоны внутри компонента затруднительно. Такой компонент обычно один на экране и не будет нервировать пользователей плиткой при загрузке. И, с большой вероятностью, его скелетон считается как «заголовочный» блок.
Конечно, никто не запрещает сделать сложную заглушку под вкладки. Но стоит ли?

Повышение детализации
Повышение детализации при скелетировании позволит пользователю лучше понять, что он увидит после загрузки данных, что положительно скажется на его опыте. Этим приёмом лучше пользоваться, когда нужно привлечь внимание пользователя или чтобы не превратить страницу в набор огромных одинаковых косточек, если таких блоков на странице много (например, карточки товаров или что‑то похожее).
Отличным примером для такого пункта можно считать график с легендой. Это хорошо считывающийся набор геометрических примитивов, а если пробросить туда и цвета легенды, то это будет 100-процентный успех.
Но стоит учитывать, что, скорее всего, график и легенда будет неразделимы и разработчикам нужно будет верстать именно заглушку, просто косточкой тут не обойтись.

Непрогнозируемые ситуации
В любом интерфейсе найдутся блоки, высоту которых сложно спрогнозировать. В таких случаях нужно узнать среднее количество строк, символов или объектов и ориентироваться на эти данные при скелетировании. Примеры таких блоков: текст, неограниченный по количеству строк или блок с чипами.

Для лучшего понимания: пример с текстом остаётся скелетоном (в зависимости от технической реализации компонента, но чаще всего в компонентах скелетона для текста можно указать количество строк), а блок с чипами уже будет являться заглушкой.
Крайне непредсказуемыми могут оказаться таблицы, если ширина их колонок жёстко не задана и если ячейки могут быть многострочными. Способы борьбы с такими ситуациями мне не известны. Если знаете — напишите в комментариях.
Повторяющиеся элементы
Если просто заменять компоненты косточками, то в итоге получаются довольно скучные скелетоны. Яркий пример — таблицы.

Но есть довольно дешёвые способы скелетировать таблицу красиво:
Делайте элементы разной ширины по определённому алгоритму, это просто реализуется в вёрстке. Например, каждый первый из пяти будет шириной в 90% от максимальной, вторая — 75, третья — 85, четвёртая — 80 и пятая — 65%.
Не игнорируйте габариты, а также узнаваемые и повторяющиеся формы вроде иконок, переключателей и прочего.
Обращайте внимание на выравнивание и старайтесь его сохранить в скелетонах.

И помните, что эти приёмы применимы не только к таблицам, особенно разноширинность. Важно, чтобы в текстовых блоках последняя строка была самой короткой, так скелетоны текста выглядят более естественно.

И ещё капельку по поводу разноширинности косточек — это действительно просто на уровне CSS.
.element:nth-child(5n+1) {
width: 90%;
}
.element:nth-child(5n+2) {
width: 75%;
}
.element:nth-child(5n+3) {
width: 85%;
}
.element:nth-child(5n+4) {
width: 80%;
}
.element:nth-child(5n+5) {
width: 65%;
}
Лайфхак с экономией времени
Скелетоны делаются по готовому дизайну, заменой контента косточками. Поэтому, если вы доверяете своим разработчикам, то предложите им обойтись без макетов. Возможно, будет достаточно обсудить и зафиксировать правила скелетирования вместо рисования новой пачки макетов, к которым всё равно потребуются уточнения.
План разработки скелетонов
Прежде чем идти к разработчикам с требованием сделать скелетоны — обязательно проконсультируйтесь и уточните возможность их реализации.
Вспомните метафору атомарного дизайна: атом → молекула → организм → шаблон → страница. Косточками становятся атомы или молекулы. А более высокие уровни лучше закрывать стилизованными заглушками.
Скелетировать надо готовые экраны, сохраняя реальную вёрстку и учитывая размеры контента.
Обсудите с разработчиками свои планы по скелетированию, всё ли реализумо и нужны ли вообще детальные макеты.
Не забывайте про консистентность интерфейса.
Не стесняйтесь заглушек, стилизованных под скелетоны.
Не забудьте согласовать анимацию одну на все экраны.