Задача
СкопированоСкелетон — это временная «заглушка». Он показывается вместо основного контента страницы на время загрузки данных. Пользователь видит не пустоту, а однотонные блоки, похожие на будущее содержимое: текст, кнопки или картинки. Часто их анимируют, что создаёт эффект загрузки. Это распространённый паттерн в веб-интерфейсах.
Скелетон выполняет ту же роль, что и спиннер, но воспринимается приятнее. Пользователь не только видит, что страница загружается, но и примерно понимает, какой контент появится после загрузки.
В рецепте разберём создание анимированного скелетона для карточки статьи:
Готовое решение
СкопированоДля создания скелетона потребуется немного HTML.
<div class="skeleton"></div>
<div class="skeleton"></div>
И чуть побольше CSS.
.skeleton { /* Общий цвет для блоков внутри скетелона */ --skeleton-bg-color: #5F377D; /* Внутренний отступ для текста внутри карточки */ --padding: 15px; /* Параметры для анимации загрузки */ --blur-width: 600%; --blur-height: 200%; --blur-start-position: 130%; --blur-end-position: 0%; --blur-background: linear-gradient( 130deg, transparent 0, transparent 35%, #18191c99 40%, #18191ce6 50%, #18191c99 60%, transparent 70%, transparent 100% ); /* Параметры для скелетона картинки */ --image-width: 100%; --image-height: 250px; --image-x: 0px; --image-y: 0px; --image-position: var(--image-x) var(--image-y); --image-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона заголовка */ --title-width: calc(80%); --title-height: 40px; --title-x: var(--padding); --title-y: calc(var(--image-height) + 25px); --title-position: var(--title-x) var(--title-y); --title-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); --title-margin-bottom: 25px; /* Общие параметры скелетонов для текстовых блоков */ --text-gap: 18px; --text-line-height: 18px; /* Параметры для скелетона первой строки текста */ --text-line-first-width: calc(100% - var(--padding) - var(--padding)); --text-line-first-height: var(--text-line-height); --text-line-first-x: var(--padding); --text-line-first-y: calc(var(--title-y) + var(--title-height) + var(--title-margin-bottom)); --text-line-first-position: var(--text-line-first-x) var(--text-line-first-y); --text-line-first-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона второй строки текста */ --text-line-second-width: calc(100% - var(--padding) - var(--padding)); --text-line-second-height: var(--text-line-height); --text-line-second-x: var(--padding); --text-line-second-y: calc(var(--text-line-first-y) + var(--text-line-first-height) + var(--text-gap)); --text-line-second-position: var(--text-line-second-x) var(--text-line-second-y); --text-line-second-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона третьей строки текста */ --text-line-third-width: 70%; --text-line-third-height: var(--text-line-height); --text-line-third-x: var(--padding); --text-line-third-y: calc(var(--text-line-second-y) + var(--text-line-second-height) + var(--text-gap)); --text-line-third-position: var(--text-line-third-x) var(--text-line-third-y); --text-line-third-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); width: 340px; height: 450px; /* Цвет фона */ background-color: #5F377D33; /* no-repeat нужен, чтобы скелетоны вложенных блоков не повторялись */ background-repeat: no-repeat; /* Задаём цвет и форму вложенных блоков */ background-image: var(--blur-background), var(--image-skeleton), var(--title-skeleton), var(--text-line-first-skeleton), var(--text-line-second-skeleton), var(--text-line-third-skeleton); /* Задаём размеры вложенных блоков */ background-size: var(--blur-width) var(--blur-height), var(--image-width) var(--image-height), var(--title-width) var(--title-height), var(--text-line-first-width) var(--text-line-first-height), var(--text-line-second-width) var(--text-line-second-height), var(--text-line-third-width) var(--text-line-third-height); /* Задаём расположение вложенных блоков */ background-position: var(--blur-start-position), var(--image-position), var(--title-position), var(--text-line-first-position), var(--text-line-second-position), var(--text-line-third-position); /* Добавляем анимацию загрузки */ animation: loading 1.8s ease-in infinite;}/* Анимация загрузки работает за счет сдвига блюра. Все остальные блоки неподвижны: не меняют своей позиции.*/@keyframes loading { to { background-position: var(--blur-end-position), var(--image-position), var(--title-position), var(--text-line-first-position), var(--text-line-second-position), var(--text-line-third-position); }}
.skeleton {
/* Общий цвет для блоков внутри скетелона */
--skeleton-bg-color: #5F377D;
/* Внутренний отступ для текста внутри карточки */
--padding: 15px;
/* Параметры для анимации загрузки */
--blur-width: 600%;
--blur-height: 200%;
--blur-start-position: 130%;
--blur-end-position: 0%;
--blur-background: linear-gradient(
130deg,
transparent 0,
transparent 35%,
#18191c99 40%,
#18191ce6 50%,
#18191c99 60%,
transparent 70%,
transparent 100%
);
/* Параметры для скелетона картинки */
--image-width: 100%;
--image-height: 250px;
--image-x: 0px;
--image-y: 0px;
--image-position: var(--image-x) var(--image-y);
--image-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона заголовка */
--title-width: calc(80%);
--title-height: 40px;
--title-x: var(--padding);
--title-y: calc(var(--image-height) + 25px);
--title-position: var(--title-x) var(--title-y);
--title-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
--title-margin-bottom: 25px;
/* Общие параметры скелетонов для текстовых блоков */
--text-gap: 18px;
--text-line-height: 18px;
/* Параметры для скелетона первой строки текста */
--text-line-first-width: calc(100% - var(--padding) - var(--padding));
--text-line-first-height: var(--text-line-height);
--text-line-first-x: var(--padding);
--text-line-first-y: calc(var(--title-y) + var(--title-height) + var(--title-margin-bottom));
--text-line-first-position: var(--text-line-first-x) var(--text-line-first-y);
--text-line-first-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона второй строки текста */
--text-line-second-width: calc(100% - var(--padding) - var(--padding));
--text-line-second-height: var(--text-line-height);
--text-line-second-x: var(--padding);
--text-line-second-y: calc(var(--text-line-first-y) + var(--text-line-first-height) + var(--text-gap));
--text-line-second-position: var(--text-line-second-x) var(--text-line-second-y);
--text-line-second-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона третьей строки текста */
--text-line-third-width: 70%;
--text-line-third-height: var(--text-line-height);
--text-line-third-x: var(--padding);
--text-line-third-y: calc(var(--text-line-second-y) + var(--text-line-second-height) + var(--text-gap));
--text-line-third-position: var(--text-line-third-x) var(--text-line-third-y);
--text-line-third-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
width: 340px;
height: 450px;
/* Цвет фона */
background-color: #5F377D33;
/* no-repeat нужен, чтобы скелетоны вложенных блоков не повторялись */
background-repeat: no-repeat;
/* Задаём цвет и форму вложенных блоков */
background-image:
var(--blur-background),
var(--image-skeleton),
var(--title-skeleton),
var(--text-line-first-skeleton),
var(--text-line-second-skeleton),
var(--text-line-third-skeleton);
/* Задаём размеры вложенных блоков */
background-size:
var(--blur-width) var(--blur-height),
var(--image-width) var(--image-height),
var(--title-width) var(--title-height),
var(--text-line-first-width) var(--text-line-first-height),
var(--text-line-second-width) var(--text-line-second-height),
var(--text-line-third-width) var(--text-line-third-height);
/* Задаём расположение вложенных блоков */
background-position:
var(--blur-start-position),
var(--image-position),
var(--title-position),
var(--text-line-first-position),
var(--text-line-second-position),
var(--text-line-third-position);
/* Добавляем анимацию загрузки */
animation: loading 1.8s ease-in infinite;
}
/*
Анимация загрузки работает за счет сдвига блюра.
Все остальные блоки неподвижны: не меняют своей позиции.
*/
@keyframes loading {
to {
background-position:
var(--blur-end-position),
var(--image-position),
var(--title-position),
var(--text-line-first-position),
var(--text-line-second-position),
var(--text-line-third-position);
}
}
Разбор решения
СкопированоРазметка
СкопированоДля создания скелетона карточки нам потребуется всего один div.
<div class="skeleton"></div>
<div class="skeleton"></div>
Вложенные блоки нарисуем на CSS.
Стили
СкопированоДля создания заглушек содержимого карточки используем линейный градиент и свойства семейства background:
Для начала заведём CSS-переменные, чтобы не повторять одни и те же значения и проще ориентироваться в коде. Параметры каждого нижеследующего блока используют размер и положение предыдущего. Важно, чтобы размеры и форма блоков были похожи на части контента, для которого делается скелетон.
.skeleton { /* Общий цвет для блоков внутри скетелона */ --skeleton-bg-color: #5F377D; /* Внутренний отступ для текста внутри карточки */ --padding: 15px; /* Параметры для анимации загрузки */ --blur-width: 600%; --blur-height: 200%; --blur-start-position: 130%; --blur-end-position: 0%; --blur-background: linear-gradient( 130deg, transparent 0, transparent 35%, #18191c99 40%, #18191ce6 50%, #18191c99 60%, transparent 70%, transparent 100% ); /* Параметры для скелетона картинки */ --image-width: 100%; --image-height: 250px; --image-x: 0px; --image-y: 0px; --image-position: var(--image-x) var(--image-y); --image-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона заголовка */ --title-width: calc(80%); --title-height: 40px; --title-x: var(--padding); --title-y: calc(var(--image-height) + 25px); --title-position: var(--title-x) var(--title-y); --title-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); --title-margin-bottom: 25px; /* Общие параметры скелетонов для текстовых блоков */ --text-gap: 18px; --text-line-height: 18px; /* Параметры для скелетона первой строки текста */ --text-line-first-width: calc(100% - var(--padding) - var(--padding)); --text-line-first-height: var(--text-line-height); --text-line-first-x: var(--padding); --text-line-first-y: calc(var(--title-y) + var(--title-height) + var(--title-margin-bottom)); --text-line-first-position: var(--text-line-first-x) var(--text-line-first-y); --text-line-first-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона второй строки текста */ --text-line-second-width: calc(100% - var(--padding) - var(--padding)); --text-line-second-height: var(--text-line-height); --text-line-second-x: var(--padding); --text-line-second-y: calc(var(--text-line-first-y) + var(--text-line-first-height) + var(--text-gap)); --text-line-second-position: var(--text-line-second-x) var(--text-line-second-y); --text-line-second-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color)); /* Параметры для скелетона третьей строки текста */ --text-line-third-width: 70%; --text-line-third-height: var(--text-line-height); --text-line-third-x: var(--padding); --text-line-third-y: calc(var(--text-line-second-y) + var(--text-line-second-height) + var(--text-gap)); --text-line-third-position: var(--text-line-third-x) var(--text-line-third-y); --text-line-third-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));}
.skeleton {
/* Общий цвет для блоков внутри скетелона */
--skeleton-bg-color: #5F377D;
/* Внутренний отступ для текста внутри карточки */
--padding: 15px;
/* Параметры для анимации загрузки */
--blur-width: 600%;
--blur-height: 200%;
--blur-start-position: 130%;
--blur-end-position: 0%;
--blur-background: linear-gradient(
130deg,
transparent 0,
transparent 35%,
#18191c99 40%,
#18191ce6 50%,
#18191c99 60%,
transparent 70%,
transparent 100%
);
/* Параметры для скелетона картинки */
--image-width: 100%;
--image-height: 250px;
--image-x: 0px;
--image-y: 0px;
--image-position: var(--image-x) var(--image-y);
--image-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона заголовка */
--title-width: calc(80%);
--title-height: 40px;
--title-x: var(--padding);
--title-y: calc(var(--image-height) + 25px);
--title-position: var(--title-x) var(--title-y);
--title-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
--title-margin-bottom: 25px;
/* Общие параметры скелетонов для текстовых блоков */
--text-gap: 18px;
--text-line-height: 18px;
/* Параметры для скелетона первой строки текста */
--text-line-first-width: calc(100% - var(--padding) - var(--padding));
--text-line-first-height: var(--text-line-height);
--text-line-first-x: var(--padding);
--text-line-first-y: calc(var(--title-y) + var(--title-height) + var(--title-margin-bottom));
--text-line-first-position: var(--text-line-first-x) var(--text-line-first-y);
--text-line-first-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона второй строки текста */
--text-line-second-width: calc(100% - var(--padding) - var(--padding));
--text-line-second-height: var(--text-line-height);
--text-line-second-x: var(--padding);
--text-line-second-y: calc(var(--text-line-first-y) + var(--text-line-first-height) + var(--text-gap));
--text-line-second-position: var(--text-line-second-x) var(--text-line-second-y);
--text-line-second-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
/* Параметры для скелетона третьей строки текста */
--text-line-third-width: 70%;
--text-line-third-height: var(--text-line-height);
--text-line-third-x: var(--padding);
--text-line-third-y: calc(var(--text-line-second-y) + var(--text-line-second-height) + var(--text-gap));
--text-line-third-position: var(--text-line-third-x) var(--text-line-third-y);
--text-line-third-skeleton: linear-gradient(var(--skeleton-bg-color), var(--skeleton-bg-color));
}
Далее зададим размеры скелетона.
.skeleton { ... width: 340px; height: 450px; ...}
.skeleton {
...
width: 340px;
height: 450px;
...
}
И опишем вложенные блоки с помощью CSS-свойств из группы background:
background— задаём цвет фона;- color background— отключаем повторение блоков с помощью значения- repeat no;- repeat background— задаём цвет и форму вложенных блоков;- image background— задаём размеры вложенных блоков;- size background— задаём расположение вложенных блоков.- position
Блоки описываем в порядке их визуального следования: сначала самый верхний (блюр), затем остальные.
.skeleton { ... /* Цвет фона */ background-color: #5F377D33; /* no-repeat нужен, чтобы скелетоны вложенных блоков не повторялись */ background-repeat: no-repeat; /* Задаём цвет и форму вложенных блоков */ background-image: var(--blur-background), var(--image-skeleton), var(--title-skeleton), var(--text-line-first-skeleton), var(--text-line-second-skeleton), var(--text-line-third-skeleton); /* Задаём размеры вложенных блоков */ background-size: var(--blur-width) var(--blur-height), var(--image-width) var(--image-height), var(--title-width) var(--title-height), var(--text-line-first-width) var(--text-line-first-height), var(--text-line-second-width) var(--text-line-second-height), var(--text-line-third-width) var(--text-line-third-height); /* Задаём расположение вложенных блоков */ background-position: var(--blur-start-position), var(--image-position), var(--title-position), var(--text-line-first-position), var(--text-line-second-position), var(--text-line-third-position); ...}
.skeleton {
...
/* Цвет фона */
background-color: #5F377D33;
/* no-repeat нужен, чтобы скелетоны вложенных блоков не повторялись */
background-repeat: no-repeat;
/* Задаём цвет и форму вложенных блоков */
background-image:
var(--blur-background),
var(--image-skeleton),
var(--title-skeleton),
var(--text-line-first-skeleton),
var(--text-line-second-skeleton),
var(--text-line-third-skeleton);
/* Задаём размеры вложенных блоков */
background-size:
var(--blur-width) var(--blur-height),
var(--image-width) var(--image-height),
var(--title-width) var(--title-height),
var(--text-line-first-width) var(--text-line-first-height),
var(--text-line-second-width) var(--text-line-second-height),
var(--text-line-third-width) var(--text-line-third-height);
/* Задаём расположение вложенных блоков */
background-position:
var(--blur-start-position),
var(--image-position),
var(--title-position),
var(--text-line-first-position),
var(--text-line-second-position),
var(--text-line-third-position);
...
}
Добавим анимацию для скелетона.
.skeleton { ... /* Добавляем анимацию загрузки */ animation: loading 1.8s ease-in infinite;}
.skeleton {
...
/* Добавляем анимацию загрузки */
animation: loading 1.8s ease-in infinite;
}
И опишем анимацию с помощью @keyframes. Для создания эффекта загрузки будем сдвигать только блюр.
/* Анимация загрузки работает за счет сдвига блюра. Все остальные блоки неподвижны.*/@keyframes loading { to { background-position: var(--blur-end-position), var(--image-position), var(--title-position), var(--text-line-first-position), var(--text-line-second-position), var(--text-line-third-position); }}
/*
Анимация загрузки работает за счет сдвига блюра.
Все остальные блоки неподвижны.
*/
@keyframes loading {
to {
background-position:
var(--blur-end-position),
var(--image-position),
var(--title-position),
var(--text-line-first-position),
var(--text-line-second-position),
var(--text-line-third-position);
}
}
В итоге получился скелетон, который довольно точно описывает форму и структуру карточки.
Подсказки
Скопировано💡 Если нужно использовать один и тот же контейнер для показа скелетона и контента, можно воспользоваться псевдоклассом :empty.
.card { /* стили контента */}.card:empty { /* стили скелетона */}
.card {
/* стили контента */
}
.card:empty {
/* стили скелетона */
}