Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Анимация появления и исчезновения

Переход из урока «Переходы» анимирует смену значения. Но у только что появившегося элемента не было «прошлого» значения — переходить не от чего. А display: none вообще не анимируется: элемент исчезает мгновенно. Современный CSS закрывает оба пробела.

@starting-style: откуда начинать

Правило @starting-style задаёт стили, с которых элемент стартует в момент появления. Браузер берёт их как «прошлое» значение и запускает переход к обычным стилям. Нажмите «Сбросить» над примером, чтобы увидеть ещё раз:

<style>
  .note {
    padding: 12px;
    background: honeydew;
    transition: opacity 1s, translate 1s;
  }
  @starting-style {
    .note { opacity: 0; translate: 0 20px; }
  }
</style>
 
<div class="note">Я плавно выехала снизу</div>

Без @starting-style блок просто появился бы сразу на месте. Здесь translate — отдельное свойство сдвига, аналог transform: translate().

Переход из display: none

display переключается рывком: значения «наполовину скрыт» не бывает. Такие свойства называют дискретными. Запись allow-discrete (полностью — transition-behavior: allow-discrete) разрешает им участвовать в переходе: при скрытии display сменится на none в самом конце, когда прозрачность уже дошла до нуля:

<style>
  .box {
    padding: 12px;
    margin-top: 8px;
    background: lavender;
    transition: opacity 0.5s, display 0.5s allow-discrete;
  }
  .box.hidden { display: none; opacity: 0; }
  @starting-style {
    .box { opacity: 0; }
  }
</style>
 
<button onclick="document.querySelector('.box').classList.toggle('hidden')">Показать / скрыть</button>
<div class="box">Плавно появляюсь и плавно исчезаю</div>

@starting-style отвечает за появление, а allow-discrete — за исчезновение. Нужны оба.

Диалог и popover

Окна <dialog> и блоки popover из урока «Раскрывающиеся блоки и диалоги» тоже скрыты через display: none. Схема та же; кроме display в переход добавляют свойство overlay — оно держит окно поверх страницы, пока идёт анимация закрытия:

<style>
  body { min-height: 200px; }
  dialog {
    opacity: 0;
    scale: 0.9;
    transition: opacity 0.3s, scale 0.3s, display 0.3s allow-discrete, overlay 0.3s allow-discrete;
  }
  dialog[open] { opacity: 1; scale: 1; }
  @starting-style {
    dialog[open] { opacity: 0; scale: 0.9; }
  }
</style>
 
<button onclick="document.querySelector('#hi').showModal()">Открыть окно</button>
<dialog id="hi">
  <p>Окно открылось плавно.</p>
  <form method="dialog"><button>Закрыть</button></form>
</dialog>

Для popover вместо dialog[open] пишут [popover]:popover-open.

Высота auto: interpolate-size

Ещё одна давняя проблема — переход к height: auto. Браузер не умел считать промежуточные значения между числом и словом auto, и блок раскрывался рывком. Строка interpolate-size: allow-keywords у :root разрешает такие переходы на всей странице. Наведите курсор на блок:

<style>
  :root { interpolate-size: allow-keywords; }
  .more {
    height: 24px;
    overflow: hidden;
    padding: 8px;
    background: lightyellow;
    transition: height 0.4s;
  }
  .more:hover { height: auto; }
</style>
 
<div class="more">Наведите — я раскроюсь.<br>Вторая строка.<br>Третья строка.<br>Четвёртая строка.</div>

На момент написания interpolate-size работает в Chrome и Edge; в других браузерах блок просто раскроется сразу, без анимации. Актуальную поддержку проверяйте на caniuse.com.

Поддержка

@starting-style и allow-discrete поддерживаются во всех основных браузерах с 2024 года. Свойство overlay есть не везде: без него закрытие окна может пройти без анимации, но окно всё равно закроется. Все эти приёмы — прогрессивное улучшение: старый браузер покажет и спрячет элемент мгновенно, как раньше.

Частая ошибка

@starting-style перед основным правилом. У селекторов одинаковая специфичность, поэтому побеждает то, что ниже: обычный стиль перекрывает стартовый, и появление идёт без анимации. Нажмите «Сбросить»:

<style>
  @starting-style { .bad { opacity: 0; } }
  .bad, .good { opacity: 1; padding: 8px; margin: 4px 0; background: mistyrose; transition: opacity 1.5s; }
  @starting-style { .good { opacity: 0; } }
</style>
 
<div class="bad">Стартовый стиль выше — появляюсь сразу</div>
<div class="good">Стартовый стиль ниже — проявляюсь плавно</div>

Пишите @starting-style после правила элемента или вкладывайте его прямо внутрь правила.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.