Эффект «скелетного» экрана: как сделать плавную загрузку профиля (HTML/CSS/JS)

Зачем нужны скелетные экраны
Скелетные экраны показывают упрощённую версию интерфейса вместо пустой страницы. Они:
- уменьшают субъективное время загрузки;
- задают ожидание пользователя о том, где появится контент;
- помогают избежать Cumulative Layout Shift (CLS), потому что область контента зарезервирована заранее.
Важно: скелет не ускоряет загрузку данных, он улучшает восприятие скорости. CLS — это изменение расположения элементов после рендера; фиксированные блоки и предустановленные размеры уменьшают CLS.
Дизайн структуры страницы
Проектирование макета помогает зафиксировать ожидания. Определите цель страницы, основную структуру и набор компонентов. Для примера используем простую карточку профиля с обложкой, аватаром, текстом и CTA-кнопками.
Шаги:
- Нарисуйте макет на бумаге или в Figma/Adobe XD.
- Определите адаптивные точки (например, 560px для мобайла).
- Проставьте размеры для ключевых областей: обложка, аватар, блоки текста.
После этого переходите к HTML-структуре.
Базовая HTML-структура
Создайте файл index.html и поместите разметку внутри контейнера с классом profile-container. Добавляйте класс skeleton к тем элементам, где хотите видеть эффект загрузки. Этот класс будет удалён через JavaScript, когда данные загрузятся.
Пример HTML:
Skeleton Screen Loading Effect
John Doe
Software Engineer @ Google || Full Stack Developer || Self Taught
Bengaluru, Karnataka, India • Contact info
Примечание: не забудьте подключить style.css и script.js в head/ниже по телу документа соответственно.
Базовые стили
Добавьте базовые правила для body: отступы, шрифт и цвет.
body {
margin: 0;
font-family: Arial, sans-serif;
color: rgba(255, 255, 255, 0.9);
background-color: #0f1112;
}Добавление эффекта загрузки (шиммер)
Скелет реализуется через класс .skeleton, невидимый контент и псевдоэлемент ::after с градиентом, который сдвигается слева направо.
.skeleton {
position: relative;
width: max-content;
overflow: hidden;
border-radius: 4px;
background-color: #1e2226 !important;
color: transparent !important;
border-color: #1e2226 !important;
user-select: none;
cursor: default;
}
.skeleton img {
opacity: 0;
}
.skeleton::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0,
rgba(255, 255, 255, 0.12) 20%,
rgba(255, 255, 255, 0.26) 60%,
rgba(255, 255, 255, 0)
);
animation: shimmer 2s infinite;
content: '';
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}Совет: уменьшите прозрачность и длительность у тёмных тем, чтобы эффект выглядел аккуратно.
Стили для изображений и контейнера
img {
width: 100%;
vertical-align: middle;
}
.profile-container {
width: 95%;
max-width: 780px;
margin: 32px auto 0;
border-radius: 8px;
background-color: #1e2226;
overflow: hidden;
position: relative;
}
.cover-img {
width: 100%;
overflow: hidden;
background-color: #1e2226;
aspect-ratio: 4 / 1;
}
.profile-img {
border-radius: 50%;
width: 160px;
height: 160px;
border: 4px solid #000;
background-color: #1e2226;
margin: 0 auto;
position: relative;
overflow: hidden;
bottom: 100px;
}Адаптивность
Добавьте медиазапросы для мобильных экранов:
@media (max-width: 560px) {
.profile-img {
width: 100px;
height: 100px;
bottom: 60px;
}
}Стили текста и CTA
.profile-text {
margin-top: -80px;
padding: 0 16px;
}
.profile-text h1 {
margin-bottom: 0;
font-size: 24px;
overflow: hidden;
}
.profile-text p { margin: 4px 0; overflow: hidden; }
.profile-text h5 {
margin-top: 4px;
font-size: 14px;
margin-bottom: 8px;
font-weight: 400;
color: #ffffff99;
overflow: hidden;
}
.profile-text a {
color: #70b5f9;
font-size: 14px;
text-decoration: none;
font-weight: 600;
}
.profile-text a:hover { text-decoration: underline; }
.profile-cta { padding: 16px 16px 32px; display: flex; }
.profile-cta a { padding: 6px 16px; border-radius: 24px; text-decoration: none; display: block; }
.message-btn { background-color: #70b5f9; color: #000; }
.more-btn { color: inherit; border: 1px solid rgba(255,255,255,0.9); margin-left: 8px; }Отключение скелета через JavaScript
Когда данные готовы, удалите класс .skeleton. Ниже простой пример: спустя 4000 мс класс удалится у всех элементов.
// script.js
const skeletons = document.querySelectorAll('.skeleton');
skeletons.forEach((skeleton) => {
setTimeout(() => {
skeleton.classList.remove('skeleton');
// при необходимости убрать inline-стили или показать img
const img = skeleton.querySelector('img');
if (img) img.style.opacity = '1';
}, 4000);
});Примечание: вместо фиктивного setTimeout используйте событие загрузки данных (fetch/axios или callback), чтобы убрать скелет сразу после получения контента.
Когда скелетные экраны лучше, а когда — нет
Важно понимать слабые стороны метода:
- Неэффективно при очень медленном соединении, когда данные загружаются десятки секунд — пользователь может устать от бесконечного шиммера.
- Если структура контента сильно варьируется, заранее зарезервировать место сложно — возможны погрешности в макете.
- Для очень простых страниц спиннер может быть легче в реализации.
Альтернативы и дополнения:
- Прогрессивные плейсхолдеры (placeholder text) для текстового контента;
- Ленивая загрузка изображений (lazy-loading) вместе со скелетом;
- Skeleton + частичная гидратация: показывать реальные данные по мере готовности блоков.
Методология внедрения (мини-план)
- Проанализировать критичные страницы и определить элементы, которые чаще всего меняют размер.
- Нарисовать скелеты для каждого ключевого блока.
- Добавить CSS-классы и шиммер.
- Подключить удаление .skeleton при успешной загрузке данных.
- Протестировать на мобильных и слабых соединениях.
- Измерить CLS и поведение с пользователями (если есть аналитика).
Критерии приёмки
- Все ключевые блоки показывают плейсхолдеры до получения данных.
- CLS измеренно не увеличивается после загрузки (визуально макет не «прыгает»).
- Изображения становятся видимыми без резких сдвигов.
- Эффект отключается сразу после получения данных (нет статического шиммера).
Чек-листы по ролям
Дизайнер:
- Определил размеры блоков для всех состояний.
- Подготовил макеты для мобильных и десктопа.
Frontend-разработчик:
- Реализовал классы .skeleton и шиммер.
- Убрал класс при получении данных, показал контент без мерцания.
QA-инженер:
- Проверил отображение при разных скоростях сети.
- Проверил отсутствие CLS при загрузке.
Тест-кейсы (ключевые)
- Проверка на 3G: страница показывает скелеты, затем данные появляются без смещения.
- Проверка с отключённым JavaScript: контент либо рендерится сервером, либо показывается запасной контент.
- Эмуляция частичной загрузки: сначала появляется текст, потом изображение — оба перехода плавные.
Ментальные модели и эвристики
- Перцепция скорости важнее реальной скорости: пользователи оценивают сайт по тому, что он показывает, а не по тому, сколько байт скачано.
- Reserve-first: резервируйте место для контента заранее, чтобы избежать CLS.
- Graceful-fallback: если скрипт не сработал, страница всё равно должна быть читаемой.
Простая схема принятия решения (Mermaid)
flowchart TD
A[Нужно ли скелетный экран?] -->|Контент динамический| B[Да]
A -->|Статическая страница| C[Нет]
B --> D{Размеры блоков предсказуемы?}
D -->|Да| E[Реализовать скелет]
D -->|Нет| F[Использовать спиннер/плейсхолдеры]Советы по оптимизации и безопасность
- Загружайте минимально необходимые данные первыми (critical path).
- Для изображений используйте размеры width/height или CSS aspect-ratio, чтобы не допустить прыжков макета.
- Не храните в скелетах чувствительную информацию.
Краткий глоссарий
- CLS: Cumulative Layout Shift, метрика непреднамеренных сдвигов макета.
- Шиммер: движущийся градиент, создающий эффект «сверкающего» плейсхолдера.
- Skeleton screen: визуальный плейсхолдер структуры контента.
Резюме
Скелетные экраны — простой и эффективный способ улучшить восприятие скорости и снизить CLS. Реализация сводится к трём шагам: проектирование макета, CSS-шиммер и отключение плейсхолдеров после загрузки данных. Протестируйте поведение на разных скоростях и устройцах и настройте время/стили для вашей аудитории.
Важно: выбирайте подходящий инструмент для вашей ситуации — иногда проще и эффективнее использовать ленивую загрузку или спиннеры для узких случаев.
Похожие материалы
Несколько аккаунтов Skype: Multi Skype Launcher
Журнал для работы: повысить продуктивность
Персональные звуки уведомлений на Android
Скачивание шоу Hulu для офлайн‑просмотра
Microsoft Start: персонализированная новостная лента