Opacity в CSS: как управлять прозрачностью элементов на сайте
Учимся создавать плавные переходы, наложения и мягкие анимации.
Свойство CSS opacity управляет прозрачностью элементов на веб-странице: оно позволяет сделать элемент невидимым или частично прозрачным, чтобы сквозь него просвечивал фон. С его помощью вы можете создавать наложения, анимации появления и исчезновения, а также различные визуальные акценты в интерфейсе.
Давайте разберёмся, как работает свойство opacity, какие значения оно принимает и в каких случаях его лучше всего применять на практике.
Содержание
- Что такое opacity и зачем оно нужно
- Как пишется свойство
- Как работает прозрачность элементов
- Особенности поведения opacity
- Влияние на дочерние элементы
- Альтернативы opacity: rgba() и hsla()
- Opacity в анимациях и переходах
- Сравнение с visibility и display
- Доступность и взаимодействие с элементами
- Типичные ошибки при работе с прозрачностью
- Лайфхак: визуализация прозрачных элементов в процессе разработки
- Практика: создаём навигационное меню с плавным появлением элементов
Что такое opacity и зачем оно нужно
Свойство CSS opacity (от англ. opacity — «непрозрачность») управляет степенью прозрачности элемента и всего его содержимого. Когда элемент становится полупрозрачным, сквозь него просвечивает фон страницы или другие элементы, которые находятся позади него.
Этот эффект широко применяется в современных интерфейсах. Он помогает создавать реакции на наведение курсора — например, затемнение или подсветку элементов. Также он позволяет реализовать плавные появления и исчезновения через переходы и анимации, добавлять полупрозрачные оверлеи и модальные окна. Кроме того, opacity позволяет временно скрыть элемент, не ломая всю вёрстку.
В отличие от display: none, свойство opacity не убирает элемент из потока и никак не влияет на расположение соседних блоков. Это делает его идеальным для интерфейсов, где нужны плавные изменения состояния без перестройки макета. Например, при наведении на карточку товара она плавно становится полупрозрачной, а остальные элементы страницы остаются на месте — никаких «прыжков».

Читайте также:
Как пишется свойство
Свойство opacity принимает значения от 0 до 1. Это коэффициент непрозрачности элемента: 0 — полная прозрачность (элемент невидим), 1 — полная непрозрачность (элемент полностью видим). Промежуточные значения создают разные степени полупрозрачности. Значение по умолчанию равно 1 — элемент полностью непрозрачен.
Дробные значения можно записывать с нулём впереди или без него. Например, opacity: 0.5 и opacity: .5 работают одинаково. Помимо числовых значений, opacity поддерживает процентную запись: 0% равно 0, а 100% соответствует 1. Однако, чтобы не путаться, советуем выбирать один вариант записи и использовать его во всём проекте.
Кроме того, свойство opacity поддерживает глобальные CSS-значения: inherit — наследует значение прозрачности от родительского элемента; initial — возвращает значение по умолчанию, то есть 1 (полная непрозрачность, как если бы свойство не было задано); unset — сбрасывает свойство к наследуемому значению от родителя, если оно установлено, или к начальному значению 1, если наследования нет.
.element {
opacity: 0; /* Полностью прозрачный */
opacity: 0.3; /* 30% видимости */
opacity: .5; /* 50% видимости */
opacity: 1; /* Непрозрачный */
opacity: 50%; /* То же, что opacity: 0.5 */
}Как работает прозрачность элементов
Браузер рисует веб-страницу не целиком, а по частям. Каждая такая часть называется композитным слоем — это отдельная область памяти, где браузер хранит готовое изображение одного или нескольких элементов. Слои нужны, чтобы при прокрутке, анимации или перемещении элемента не перерисовывать всю страницу — достаточно обновить только нужный слой. Так всё работает быстрее и плавнее.
Когда вы задаёте элементу свойство opacity со значением меньше 1, браузер переносит его на отдельный композитный слой. Сначала браузер полностью отрисовывает элемент, затем применяет к нему прозрачность и после этого накладывает готовый слой на то, что находится ниже на странице.
Создадим синий блок с текстом «Фоновый элемент» и поверх него разместим красный блок с opacity: 0.7, слегка сдвинутый в сторону. Красный блок полупрозрачен — сквозь него виден синий фон и текст. В зоне перекрытия цвета смешиваются, образуя промежуточный оттенок.
Вот как это выглядит в браузере.

Чтобы попробовать, вставьте этот фрагмент в HTML-разметку:
<div class="container">
<div class="background">Фоновый элемент</div>
<div class="overlay">Полупрозрачное наложение</div>
</div>Затем добавьте стили:
.container {
position: relative;
background: #f0f0f0;
width: 300px;
height: 300px;
}
.background {
padding: 20px;
background: #3498db;
color: #ffffff;
height: 100%;
width: 100%;
}
.overlay {
position: absolute;
top: 30px;
left: 30px;
right: 0;
bottom: 0;
background: rgba(231, 76, 60, 0.7);
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
}Когда у элемента значение opacity становится меньше 1, браузер переносит его на отдельный композитный слой, и содержимое элемента не может выйти за границы слоя по оси Z. Даже если дочерний элемент имеет position: absolute и большой z-index, он останется внутри полупрозрачного родителя и не сможет перекрыть другие элементы.
Например, если внутри полупрозрачного красного блока сделать всплывающее меню, оно не сможет перекрыть элементы, которые находятся вне красного блока: оно будет видно только внутри него.
Браузер создаёт композитные слои не только для прозрачности. Он использует их также для элементов с position: fixed, трансформациями (transform), анимациями и многими другими свойствами. Это позволяет браузеру обновлять только изменённые части страницы, экономить ресурсы и обеспечивать плавность анимаций и переходов.

Читайте также:
Особенности поведения opacity
Браузер обрабатывает opacity особым образом: даже если элемент полностью прозрачен (opacity: 0), он остаётся частью страницы и продолжает реагировать на действия пользователя. По невидимой кнопке можно кликнуть, перевести на неё фокус или прокрутить её содержимое. Это отличает opacity от display: none — последнее свойство удаляет элемент из потока, и он перестаёт принимать события.
Возьмём кнопку с opacity: 0, но с отступами и курсором pointer. Она не видна, но занимает место. При наведении курсора срабатывает :hover, и прозрачность меняется на 0.5 — кнопка проявляется. Это доказывает, что браузер продолжает отслеживать положение курсора и обрабатывать события, даже если элемент визуально отсутствует.
Посмотрите, как это работает.

Фрагмент HTML-разметки:
<button class="invisible-button">Невидимая кнопка</button> <p>Попробуйте кликнуть в области выше</p>CSS-стили:
.invisible-button {
opacity: 0;
padding: 20px;
background: #3498db;
color: white;
border: none;
cursor: pointer;
}
.invisible-button:hover {
opacity: 0.5;
}Добавьте кнопке дополнительный класс:
<button class="invisible-button non-interactive">Невидимая кнопка</button>
<p>Попробуйте кликнуть в области выше</p>Теперь в стилях добавим дополнительное свойство и отключим любое взаимодействие с прозрачным элементом — чтобы при наведении курсора невидимая кнопка никак не отреагировала на ваши действия.
.invisible-button {
opacity: 0;
padding: 20px;
background: #3498db;
color: white;
border: none;
cursor: pointer;
}
.invisible-button:hover {
opacity: 0.5;
}
.non-interactive {
opacity: 0;
pointer-events: none;
}Ещё одна особенность opacity — возможность плавной анимации через transition. Это удобно для эффектов появления и исчезновения элементов. Достаточно задать начальное значение opacity: 0, а при наведении или другом событии изменить его на opacity: 1. Браузер рассчитает промежуточные значения и создаст плавный переход.

Чтобы попробовать, вставьте этот фрагмент HTML-разметки.
<button class="fade-element">Невидимая кнопка</button>
<p>Попробуйте кликнуть в области выше</p>Затем добавьте CSS-стили:
.fade-element {
opacity: 0;
transition: opacity 0.3s ease-in-out;
padding: 20px;
background: #3498db;
color: white;
border: none;
cursor: pointer;
}
.fade-element:hover {
opacity: 1;
}Элементы с opacity остаются доступными для программ экранного доступа (скринридеров), голосового управления и навигации с клавиатуры. Пользователи с нарушениями зрения смогут обнаружить элемент и взаимодействовать с ним, даже если он визуально невидим. То есть скринридер прочитает текст кнопки, даже если она прозрачна.
Чтобы полностью скрыть элемент от программ экранного доступа, используйте атрибут aria-hidden="true». Скринридер проигнорирует такой элемент, и пользователь не узнает о его существовании.
Влияние на дочерние элементы
Когда вы задаёте opacity родительскому элементу, прозрачность применяется ко всем его потомкам. Браузер отрисовывает контейнер со всем содержимым — заголовками, текстом, кнопками и другими вложенными элементами, а затем применяет эффект прозрачности к получившейся картинке целиком. Поэтому увеличить непрозрачность отдельного дочернего элемента невозможно — даже если задать ему opacity: 1, его видимость останется такой же, как у родителя.
Например, если контейнеру задать opacity: 0.3, то весь его контент станет прозрачным на 70%. В этом opacity отличается от большинства CSS-свойств: оно не переопределяется на уровне дочерних элементов.

Фрагмент HTML-разметки:
<div class="parent">
<h2>Заголовок</h2>
<p>Абзац текста</p>
<button>Кнопка</button>
</div>CSS-стили:
.parent {
opacity: 0.3;
background: #3498db;
padding: 20px;
color: white;
}Чтобы сохранить полную непрозрачность содержимого при полупрозрачном фоне, можно вынести содержимое за пределы прозрачного контейнера или использовать псевдоэлемент :before.
Попробуем первый способ. Для этого создадим общего родителя, внутри которого разместим два элемента: один — абсолютно позиционированный блок для полупрозрачного фона, второй — блок с position: relative и более высоким z-index для контента. Фон станет полупрозрачным, а текст и элементы останутся полностью видимыми.
Обновите HTML-разметку:
<div class="container">
<div class="background"></div>
<div class="content">
<h2>Этот заголовок останется непрозрачным</h2>
</div>
</div>Затем пропишите следующие стили:
.container {
position: relative;
}
.background {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #3498db;
opacity: 0.2;
}
.content {
position: relative;
z-index: 1;
padding: 20px;
}Вы увидите непрозрачный заголовок на почти прозрачном фоне.

Скриншот: Google Chrome / Skillbox Media
Теперь попробуем второй способ — с псевдоэлементом :before. Псевдоэлемент позиционируется абсолютно и растягивается на всю площадь родителя. Ему задаются нужный цвет и opacity, после чего он отправляется назад с помощью z-index: -1. Содержимое элемента остаётся на переднем плане и сохраняет полную непрозрачность.
Замените предыдущий HTML-фрагмент этим кодом:
<div class="element">
<h2>Этот заголовок останется непрозрачным</h2>
</div>После этого обновите стили и посмотрите на результат в браузере:
.element {
position: relative;
padding: 20px;
}
.element::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #3498db;
opacity: 0.2;
z-index: -1;
}Альтернативы opacity: rgba() и hsla()
Если нужно сделать прозрачным только фон, текст или границу — но не весь элемент целиком, — используйте цветовые функции rgba() и hsla(). В отличие от opacity, они применяют прозрачность к конкретному свойству (например, background или color), не влияя на дочерние элементы и не создавая отдельный композитный слой.
Функция rgba() задаёт цвет через красный (red), зелёный (green) и синий (blue) каналы — каждый принимает значения от 0 до 255. Четвёртый параметр (альфа-канал) управляет прозрачностью и принимает значения от 0 (прозрачный) до 1 (непрозрачный).
Например, rgba(52, 152, 219, 0.5) создаст синий цвет с прозрачностью 50%. С rgba() можно делать полупрозрачными фон, текст или границу независимо друг от друга, не трогая остальные свойства элемента.
Вставьте в редактор следующий HTML-фрагмент:
<div class="element">Пример текста</div>После этого пропишите стили и откройте страницу в браузере:
body {
background: #333;
/* Тёмный фон страницы */
}
.element {
background: rgba(52, 152, 219, 0.7);
/* Полупрозрачный синий фон */
color: rgba(0, 0, 0, 0.8);
/* Полупрозрачный чёрный текст */
border: 2px solid rgba(255, 255, 255, 0.5);
/* Полупрозрачная белая рамка */
}Функция hsla() работает аналогично, но использует модель HSL: оттенок задаётся в градусах (0–360), насыщенность и яркость — в процентах (0–100%), а альфа-канал отвечает за прозрачность.
Чтобы убедиться, замените в стилях значение для класса .element:
.element {
background: hsla(210, 70%, 53%, 0.7);
/* Полупрозрачный синий фон */
color: hsla(0, 0%, 0%, 0.8);
/* Полупрозрачный чёрный текст */
border: 2px solid hsla(0, 0%, 100%, 0.5);
/* Полупрозрачная белая рамка */
}Главное отличие от opacity — точечный контроль прозрачности. С rgba() и hsla() вы можете сделать фон полупрозрачным, а текст, кнопки и другие элементы оставить видимыми: они не наследуют прозрачность родителя. Если задать карточке полупрозрачный белый фон (rgba(255, 255, 255, 0.3)), добавить размытие через backdrop-filter: blur (10px) и лёгкую тень, получится эффект матового стекла. При этом весь внутренний контент останется чётким и читаемым.

Чтобы создать такую карточку, добавьте разметку:
<div class="card">
<h3>Заголовок карточки</h3>
<p>Текст остаётся читаемым</p>
<button type="button">Кнопка действия</button>
</div>Затем добавьте стили и откройте страницу в браузере:
body {
background: #008001;
margin: 0;
padding: 40px;
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
}
.card {
background: #c0dabc;
padding: 30px;
border-radius: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 600px;
}
h3 {
color: #333;
margin-top: 0;
margin-bottom: 15px;
font-size: 18px;
}
p {
color: #333;
line-height: 1.6;
margin-bottom: 20px;
font-size: 14px;
}
button {
background: #5a8c5a;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #4a7a4a;
}Opacity в анимациях и переходах
Браузер обрабатывает свойство opacity на уровне графического процессора, что экономит ресурсы системы. Благодаря этому оно отлично подходит для анимаций: переходы получаются плавными даже на слабых устройствах. Именно поэтому эффекты появления и исчезновения на основе прозрачности так популярны в интерфейсах.
Самый простой эффект плавного появления создаётся через transition. Элемент изначально имеет opacity: 0.3, а при наведении курсора или добавлении класса значение меняется на opacity: 1. Браузер автоматически рассчитывает промежуточные значения и создаёт плавный переход.
HTML-разметка:
<div class="pulsing-element fade-element">
?
</div>CSS-стили:
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #f5f5f5;
font-family: Arial, sans-serif;
}
.fade-element {
opacity: 0.3;
transition: opacity 0.3s ease-in;
padding: 30px 50px;
background: #3498db;
color: white;
font-size: 18px;
border-radius: 8px;
cursor: pointer;
}
.fade-element:hover {
opacity: 1;
}Для более сложных анимаций используют CSS-правило @keyframes. Например, эффект пульсации циклически меняет прозрачность от 1 до 0.5 и обратно. Чтобы применить анимацию к элементу, используйте свойство animation с указанием длительности и числа повторений.
Попробуйте заменить все предыдущие стили на следующий код:
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #f0f0f0;
}
.pulsing-element {
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 80px;
color: white;
cursor: pointer;
background: #3498db;
border: none;
border-radius: 50%;
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}Когда вы запустите код в браузере, то увидите такую анимацию.

Свойство opacity часто комбинируют с другими свойствами. Например, если одновременно изменить прозрачность и масштаб через transform: scale(), то у вас получится эффект открытия модального окна — элемент плавно появится из центра и станет полностью видимым.

Скриншот: Google Chrome / Skillbox Media
Чтобы повторить, вставьте этот код в пустой HTML-файл:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Модальное окно с анимацией</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #f0f0f0;
font-family: Arial, sans-serif;
}
.modal {
opacity: 0;
transform: scale(0.8);
transition: opacity 0.3s ease-out, transform 0.3s ease-out;
background: white;
padding: 40px;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
max-width: 400px;
}
.modal.show {
opacity: 1;
transform: scale(1);
}
.modal h2 {
margin-top: 0;
color: #333;
}
.modal p {
color: #666;
line-height: 1.6;
}
.modal button {
background: #3498db;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
margin-top: 20px;
}
.modal button:hover {
background: #2980b9;
}
.trigger-btn {
position: fixed;
top: 20px;
left: 20px;
background: #3498db;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
}
.trigger-btn:hover {
background: #2980b9;
}
</style>
</head>
<body>
<button class="trigger-btn" onclick="toggleModal()">Показать модальное окно</button>
<div class="modal" id="modal">
<h2>Модальное окно</h2>
<p>Это модальное окно появляется с эффектом плавного увеличения и изменения прозрачности.</p>
<button onclick="toggleModal()">Закрыть</button>
</div>
<script>
function toggleModal() {
const modal = document.getElementById('modal');
modal.classList.toggle('show');
}
</script>
</body>
</html>Функция времени — ease-in, ease-out или linear — определяет, как именно изменяется скорость анимации на протяжении её выполнения. Плавное ускорение в начале (ease-in) или замедление в конце (ease-out) делает анимацию более естественной и приятной для восприятия. Браузеры особенно эффективно оптимизируют свойства opacity и transform, обрабатывая их на уровне графического процессора (GPU).

Скриншот: Google Chrome / Skillbox Media
При работе с несколькими элементами можно добавить задержку через transition-delay. Например, в списке каждый пункт получает свою индивидуальную задержку: первый элемент — 0,1 секунды, второй — 0,2 секунды, третий — 0,3 секунды и так далее. Когда вы добавляете класс .show к родительскому контейнеру, элементы начинают появляться последовательно один за другим — создаётся эффект каскадного появления или волны. Посмотрите, как это выглядит в браузере.

Вот HTML-разметка, которая поможет вам создать такой же список:
<button onclick="showList()">Показать список</button>
<ul class="list-container">
<li class="list-item">Первый элемент</li>
<li class="list-item">Второй элемент</li>
<li class="list-item">Третий элемент</li>
</ul>CSS-стили:
.list-item {
padding: 10px;
background: #f0f0f0;
margin: 5px 0;
border-radius: 4px;
opacity: 0;
transition: opacity 0.3s ease-out;
}
.list-item:nth-child(1) {
transition-delay: 0.1s;
}
.list-item:nth-child(2) {
transition-delay: 0.2s;
}
.list-item:nth-child(3) {
transition-delay: 0.3s;
}
.list-container.show .list-item {
opacity: 1;
}Также вам понадобится немного JavaScript:
function showList() {
document.querySelector(".list-container").classList.add("show");
}Сравнение с visibility и display
В CSS есть три основных способа скрыть элемент на странице: opacity, visibility и display. Каждый из них подходит для разных задач.
Свойство opacity: 0 делает элемент невидимым, но при этом он продолжает занимать место в макете и остаётся интерактивным. То есть элемент всё ещё реагирует на события мыши, может получать фокус при навигации с клавиатуры и доступен для скринридеров.
Свойство visibility: hidden также сохраняет место элемента в потоке, но отключает его интерактивность. Элемент не реагирует на события мыши, не получает фокус и становится невидимым для скринридеров.
Свойство display: none полностью удаляет элемент из потока документа — браузер не резервирует для него место в макете и не учитывает его при расчёте положения других элементов. Такой элемент не реагирует на действия пользователя и невидим для скринридеров.
Доступность и взаимодействие с элементами
Элементы с opacity: 0 остаются доступными для скринридеров и клавиатурной навигации, даже когда они визуально невидимы. Это полезно, если нужно скрыть информацию от зрячих пользователей.
Например, это полезно для уведомлений об ошибках валидации форм: они озвучиваются скринридером сразу после отправки формы, но визуально скрыты до момента появления ошибки. Также это подходит для динамически обновляемого контента — например, счётчиков непрочитанных сообщений или уведомлений о новых данных.
Однако в других случаях это может создать проблемы с доступностью. Например, пользователь, перемещающийся по странице с помощью клавиатуры, может случайно установить фокус на невидимую кнопку и не понять, где он находится. Кроме того, скринридер может озвучить содержимое фонового изображения или другого декоративного элемента, который должен быть скрыт для ассистивных технологий.
Для управления доступностью прозрачных элементов для ассистивных технологий используются ARIA-атрибуты. Чтобы увидеть их в действии, вставьте в разметку код из следующего примера. Если вы откроете страницу в браузере, то ничего не увидите. Однако код там есть — чтобы в этом убедиться, воспользуйтесь инспектором элементов.
<!-- Элемент невидим визуально, но доступен для скринридеров -->
<div class="status-message" style="opacity: 0;">
Сообщение о статусе операции
</div>
<!-- Элемент невидим и для скринридеров тоже -->
<div class="decorative-overlay" style="opacity: 0;" aria-hidden="true">
Декоративный элемент
</div>При работе с интерактивными элементами важно следить за контрастом в видимом состоянии. Кнопки или поля ввода с высокой прозрачностью плохо различимы — особенно на сложных фонах или при наложении на изображения. Чем прозрачнее элемент, тем сложнее его воспринимать. Это касается в первую очередь пользователей с нарушениями зрения, а также тех, кто работает в условиях яркого освещения или на экранах с низким качеством цветопередачи.

Скриншот: Google Chrome / Skillbox Media
Это особенно заметно в формах: полупрозрачные поля ввода затрудняют чтение введённого текста, усложняют проверку данных перед отправкой и замедляют процесс заполнения. Например, поле ввода с opacity: 0.5 на светлом фоне может выглядеть стильно, но текст в нём станет трудночитаемым. В результате пользователю придётся прищуриваться, чтобы убедиться, что он правильно ввёл email.
Для модальных окон, всплывающих подсказок и других подобных элементов советуем сочетать opacity с pointer-events. В неактивном состоянии элемент получает opacity: 0 и pointer-events: none — он становится невидимым и некликабельным, что предотвращает случайные клики. При активации возвращаются opacity: 1 и pointer-events: auto — элемент становится видимым и интерактивным.
Типичные ошибки при работе с прозрачностью
Непонимание наследования прозрачности. Разработчики часто пытаются сделать дочерний элемент менее прозрачным, чем родительский. Но это не работает: дочерний элемент наследует прозрачность родителя и поэтому не может быть менее прозрачным.
/* Неправильно — так не сработает */
.parent {
opacity: 0.3;
}
.child {
opacity: 1; /* Дочерний элемент останется полупрозрачным */
}Правильное решение — использовать rgba() для фона родительского элемента вместо opacity или изменить структуру HTML так, чтобы дочерний элемент находился за пределами прозрачного родителя.
Вот пример реализации с помощью rgba():
.parent {
background: rgba(52, 152, 219, 0.3);
}
.child {
/* Дочерний элемент останется непрозрачным */
}Другой вариант — изменить структуру разметки за счёт вынесения прозрачного фона в отдельный элемент. Тогда прозрачность будет применяться только к фону, а содержимое останется видимым.
<div class="wrapper">
<!-- Этот элемент отвечает только за фон и может быть полупрозрачным -->
<div class="background"></div>
<!-- А здесь находится контент, который должен оставаться полностью непрозрачным -->
<div class="content">
<p>Этот текст остаётся полностью непрозрачным</p>
</div>
</div>Ошибки с контекстом наложения. Элемент с opacity, отличной от 1, автоматически создаёт новый контекст наложения. Из-за этого дочерние элементы с высоким z-index не смогут выйти за пределы родительского контейнера и наложиться поверх элементов, находящихся снаружи, — даже если z-index дочерних элементов имеет большее значение.
В результате это часто приводит к тому, что всплывающие блоки, выпадающие меню, подсказки или модальные окна оказываются под другими элементами страницы, которые должны были быть ниже.
/* Неправильно — popup не сможет перекрыть overlay */
.overlay {
position: absolute;
opacity: 0.9; /* Создаёт новый контекст наложения */
z-index: 10;
}
.popup {
position: absolute;
z-index: 20; /* Не перекроет overlay, если вложен внутрь него */
}Правильное решение зависит от ситуации и может быть реализовано несколькими способами. Самый простой вариант — вынести проблемный элемент за пределы прозрачного контейнера.
Так это может выглядеть в HTML-разметке:
<!-- Правильно — popup и overlay на одном уровне -->
<div class="page">
<div class="content-with-overlay">
<div class="overlay"></div>
<div class="content">Основной контент</div>
</div>
<div class="popup">Всплывающее окно</div>
</div>Такой может быть стилизация:
.overlay {
position: absolute;
opacity: 0.9;
z-index: 10;
}
.popup {
position: fixed; /* Или absolute относительно .page */
z-index: 20; /* Теперь корректно работает */
}Следующий способ — создать прозрачный псевдоэлемент вместо применения opacity к самому контейнеру:
.overlay {
position: relative;
/* Свойство opacity здесь не используется */
}
.overlay::before {
content: '';
position: absolute;
inset: 0;
background: black;
opacity: 0.9; /* Прозрачность только у фона */
z-index: -1;
}
.popup {
position: absolute;
z-index: 20; /* Перекрывает внешние элементы */
}Если прозрачность необходима только для фона, можно использовать цвет с альфа-каналом (rgba или hsla) вместо свойства opacity. Так вы сможете сделать прозрачным только фон, не затрагивая содержимое.
/* Правильно — контекст наложения не создаётся */
.overlay {
position: absolute;
background: rgba(0, 0, 0, 0.9); /* Вместо opacity */
z-index: 10;
}
.popup {
position: absolute;
z-index: 20; /* Теперь работает как ожидается */
}Непродуманная анимация множества элементов. Если вы одновременно анимируете десятки или сотни элементов по opacity без подготовки, браузер может дёргаться. Это происходит из-за большого объёма вычислений по смешиванию цветов (альфа-блендинга) или постоянного перемещения элементов в композитные слои и обратно.
/* Неправильно — без подготовки может лагать */
.many-items {
opacity: 0;
transition: opacity 0.3s;
}Правильное решение будет заключаться в том, чтобы явным образом подготовить элементы к анимации с помощью свойства will-change или transform: translateZ (0). Браузер заранее создаст композитный слой для каждого элемента, и анимация будет проходить без лагов и перерисовок.
/* Правильно — подсказываем браузеру заранее */
.many-items {
opacity: 0;
transition: opacity 0.3s;
will-change: opacity; /* Cовременный способ */
}
/* Альтернатива для старых браузеров */
.many-items-legacy {
opacity: 0;
transition: opacity 0.3s;
transform: translateZ(0); /* Принудительно создаём композитный слой */Также важно динамически включать will-change перед анимацией и выключать после её завершения: так вы избежите удержания лишних композитных слоёв в памяти, которое может снизить производительность.
// Перед анимацией включаем will-change
function fadeIn(el) {
el.style.willChange = 'opacity';
// Запускаем анимацию появления
requestAnimationFrame(() => {
el.classList.add('show'); // В CSS: .show { opacity: 1; }
});
// После завершения перехода снимаем will-change
el.addEventListener('transitionend', function onEnd() {
el.style.willChange = 'auto';
el.removeEventListener('transitionend', onEnd);
});
}Лайфхак: визуализация прозрачных элементов в процессе разработки
Основная проблема при работе с opacity — элементы с низкой прозрачностью часто теряются в процессе разработки. Становится сложно найти их в макете страницы и позиционировать относительно других элементов или заметить случайно оставленные невидимыми блоки, которые могут мешать взаимодействию с интерфейсом.
Чтобы избежать этих проблем, добавьте в HTML-разметку временный класс .debug, который будет подсвечивать прозрачные элементы:
<!-- Во время разработки добавьте класс debug -->
<div class="container debug">
<div class="opacity-element">
Этот элемент теперь подсвечен красной обводкой.
</div>
</div>CSS-стили:
/* Обычные стили */
.opacity-element {
opacity: 0.2;
background: rgba(255, 0, 0, 0.1);
}
/* Временный debug-класс — только для разработки */
/* !important нужен, чтобы перебить inline-стили и высокую специфичность */
.debug .opacity-element {
outline: 2px dashed red !important;
background: rgba(255, 0, 0, 0.1) !important;
}После настройки элемента просто удалите класс .debug из разметки:
<!-- Перед коммитом удалите класс debug -->
<div class="container">
<div class="opacity-element">
Этот элемент теперь не подсвечен красной обводкой.
</div>
</div>А вот CSS-правило .debug .opacity-element можно оставить в файле. Если в HTML нет класса .debug, правило не применится и никак не повлияет на страницу. Зато при следующей отладке оно уже будет готово к использованию. Только желательно добавить комментарий, чтобы другие разработчики понимали назначение этого куска кода.
Практика: создаём навигационное меню с плавным появлением элементов
В этом разделе мы создадим адаптивное меню, которое скрывается под кнопкой и раскрывается при нажатии. Такой подход используется в мобильных версиях сайтов, где важно экономить место на экране.
Ключевая особенность реализации — комбинация трёх CSS-свойств для плавной анимации: opacity управляет прозрачностью меню, pointer-events блокирует клики по скрытым элементам, а transform добавляет движение при раскрытии. Пункты появляются поочерёдно — каждый следующий с большей задержкой, создавая эффект каскада.

Чтобы реализовать такое меню, вставьте следующий код в пустой HTML-файл и запустите его в браузере:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Навигационное меню</title>
<style>
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f6f8fb;
padding: 50px 20px;
}
/* Контейнер для кнопки и меню */
.main-navigation {
position: relative;
max-width: 560px;
}
/* Кнопка с иконкой ☰ */
.menu-toggle {
width: 44px;
height: 44px;
background: #4c9aff;
color: white;
border: none;
border-radius: 10px;
font-size: 22px;
cursor: pointer;
}
.menu-toggle:hover {
background: #3a89f0;
}
/* Само меню (изначально спрятано) */
.menu-items {
list-style: none;
margin: 12px 0 0;
padding: 0;
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border-radius: 12px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
/* Делаем меню невидимым и некликабельным */
opacity: 0;
pointer-events: none;
transform: translateY(-10px);
/* Поднимаем вверх */
transition: opacity 0.3s ease, transform 0.3s ease;
}
/* Стили для открытого меню (есть класс open) */
.main-navigation.open .menu-items {
opacity: 1;
pointer-events: auto;
/* Теперь можно кликать */
transform: translateY(0);
/* Опускаем на место */
}
.menu-items li {
border-bottom: 1px solid #f0f0f0;
}
.menu-items li:last-child {
border-bottom: none;
}
.menu-items a {
display: block;
padding: 14px 20px;
color: #333;
text-decoration: none;
opacity: 0;
transform: translateX(-20px);
transition: opacity 0.3s ease, transform 0.3s ease, background 0.2s ease;
}
.main-navigation.open .menu-items a {
opacity: 1;
transform: translateX(0);
}
/* Каждая ссылка появляется с задержкой */
.main-navigation.open .menu-items li:nth-child(1) a {
transition-delay: 0.06s;
}
.main-navigation.open .menu-items li:nth-child(2) a {
transition-delay: 0.12s;
}
.main-navigation.open .menu-items li:nth-child(3) a {
transition-delay: 0.18s;
}
.main-navigation.open .menu-items li:nth-child(4) a {
transition-delay: 0.24s;
}
.menu-items a:hover {
background: #f5f9ff;
color: #4c9aff;
}
</style>
</head>
<body>
<nav class="main-navigation">
<button class="menu-toggle">☰</button>
<ul class="menu-items">
<li><a href="#home">Главная</a></li>
<li><a href="#about">О нас</a></li>
<li><a href="#services">Услуги</a></li>
<li><a href="#contact">Контакты</a></li>
</ul>
</nav>
<script>
// Находим элементы на странице
const nav = document.querySelector('.main-navigation');
const toggleButton = document.querySelector('.menu-toggle');
const menuLinks = document.querySelectorAll('.menu-items a');
// Клик по кнопке — открываем или закрываем меню
toggleButton.addEventListener('click', function () {
nav.classList.toggle('open');
});
// Клик по любой ссылке — закрываем меню
menuLinks.forEach(function (link) {
link.addEventListener('click', function () {
nav.classList.remove('open');
});
});
// Клик мимо меню — тоже закрываем
document.addEventListener('click', function (event) {
if (!nav.contains(event.target)) {
nav.classList.remove('open');
}
});
// Кнопка Escape — закрываем меню
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') {
nav.classList.remove('open');
}
});
</script>
</body>
</html>В этом коде меню изначально скрыто с помощью opacity: 0 и pointer-events: none, а также смещено вверх на 10 пикселей. Когда пользователь нажимает на кнопку с иконкой «☰», JavaScript добавляет класс open к элементу навигации. Это запускает CSS-анимацию: меню плавно опускается на своё место (translateY (0)) и становится видимым (opacity: 1). Свойство pointer-events меняется на auto — элемент снова становится интерактивным и доступным для кликов мышью.
Каждая ссылка внутри меню также сначала прозрачна (opacity: 0) и смещена влево (translateX (-20px)). При открытии меню пункты поочерёдно проявляются и выезжают на место: каждый следующий — с небольшой задержкой благодаря разным значениям transition-delay.
Меню закрывается в четырёх случаях: при клике на один из пунктов, при клике в любом месте за пределами меню, при нажатии клавиши Escape или при повторном нажатии на кнопку с иконкой «☰».
Закрытие происходит с той же плавной анимацией, только в обратном порядке: пункты поочерёдно уезжают влево и исчезают, а само меню поднимается на 10 пикселей и становится полностью прозрачным.
Полезные ссылки
- MDN: CSS opacity — официальная документация по свойству opacity.
- MDN: Использование CSS-переходов — руководство по созданию плавных анимаций с помощью transition.
- MDN: CSS pointer-events — документация по управлению интерактивностью элементов.
- web.dev: Animations Guide — инструкция по созданию высокопроизводительных CSS-анимаций.
Больше интересного про код — в нашем телеграм-канале. Подписывайтесь!
