Плавный скролл на CSS: scroll-behavior, scroll-margin и JavaScript

Плавный скролл на CSS включается одной строкой: html { scroll-behavior: smooth; }.

Разобрали, как не спрятать заголовок под фиксированной шапкой, как учесть настройку уменьшения анимации, когда нужен JavaScript со своей длительностью и почему прокрутка иногда остаётся резкой.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 3067 статей автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3721 экспертное мнение Подписаться на Телеграм-канал
Обложка: Плавный скролл на CSS: scroll-behavior, scroll-margin и JavaScript

Чтобы сделать плавный скролл на CSS, добавьте в стили одну строку: html { scroll-behavior: smooth; }. После этого клик по якорной ссылке вида <a href="#price"> не перебрасывает страницу рывком, а прокручивает её к нужному блоку. JavaScript для этого не нужен.

Ниже разберём, куда именно ставить свойство, как не спрятать заголовок под фиксированной шапкой и когда без JavaScript всё-таки не обойтись. Если вы только начинаете со стилями, сначала пригодится разбор что такое CSS и как он устроен.

CheckroiCheckroiПодборка курсов по CSS181 курс • 37 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Как сделать плавный скролл к якорю на чистом CSS

Рой плавно едет на подъёмнике вдоль длинной страницы сайта

Плавная прокрутка к якорю собирается из трёх частей: ссылки, блока с id и одного свойства в CSS. Порядок такой.

  1. Дайте целевому блоку атрибут id, например <section id="price">.
  2. Поставьте на него ссылку с решёткой: <a href="#price">Цены</a>. Как устроены сами ссылки и атрибут href, подробно описано в статье про тег a в HTML.
  3. Добавьте в CSS правило для корневого элемента html.
  4. Обновите страницу и кликните по ссылке: страница поедет к блоку, а не прыгнет.
<nav>
  <a href="#about">О нас</a>
  <a href="#price">Цены</a>
</nav>

<section id="about">…</section>
<section id="price">…</section>
html {
  scroll-behavior: smooth;
}

У свойства два рабочих значения. auto стоит по умолчанию и прокручивает мгновенно, smooth включает анимацию. Если правило не срабатывает, первым делом проверьте, что файл стилей вообще подключён: три способа и быстрая проверка есть в инструкции как подключить CSS к HTML.

Скорость и кривую анимации в CSS не задать. По справке MDN, функцию сглаживания и длительность определяет браузер.

Плавная прокрутка внутри блока

Свойство работает не только для всей страницы. Его можно повесить на любой контейнер со своей полосой прокрутки: список сообщений в чате, ленту карточек, длинную таблицу.

.chat {
  height: 400px;
  overflow-y: auto;
  scroll-behavior: smooth;
}

Здесь плавной станет прокрутка только внутри .chat. На саму страницу правило не влияет, и наоборот: scroll-behavior у html не делает плавными вложенные контейнеры.

Канал основателя Checkroi Вани Буявца3 700 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Как не спрятать заголовок под фиксированной шапкой

Рой радуется, что заголовок раздела виден под шапкой

Частая картина: шапка сайта закреплена через position: fixed, вы кликаете по якорю, а заголовок раздела уезжает под неё. Браузер прокрутил блок к верхнему краю окна, но этот край закрыт шапкой.

Решение: задать отступ прокрутки. Подходят два свойства, результат у них одинаковый, разница в том, куда их ставить.

Свойство Куда ставить Когда удобнее
scroll-padding-top на html, один раз шапка одной высоты на всём сайте
scroll-margin-top на целевой блок отступ нужен только у части разделов или разный
html {
  scroll-behavior: smooth;
  scroll-padding-top: 80px; /* высота шапки */
}

/* или точечно */
#price {
  scroll-margin-top: 80px;
}

Если высота шапки меняется на мобильных, вынесите её в переменную и переопределите внутри медиа-запроса. Как писать такие условия под разные экраны, показано в статье про медиа-запросы в CSS. Описание свойства и примеры есть в справке MDN по scroll-margin-top.

Плавный скролл и настройка уменьшения анимации

У части людей анимация прокрутки вызывает дискомфорт, поэтому в Windows, macOS, iOS и Android есть настройка доступности, которая просит сайты убрать лишнее движение. CSS её видит через медиа-запрос prefers-reduced-motion.

Правильнее включать плавность только тем, кто такую настройку не трогал:

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

Если настройка включена, этот код не применится, и якорь сработает мгновенно. Значения no-preference и reduce разобраны в справке MDN по prefers-reduced-motion.

Плавная прокрутка через JavaScript

CSS срабатывает на переходы по якорям и на прокрутку, которую запускает скрипт. Когда скроллить нужно по событию (кнопка возврата наверх, переход к ошибке в форме, автопрокрутка чата), используют методы DOM. Если с объектной моделью документа вы пока не знакомы, начните со статьи про DOM-дерево в JavaScript.

window.scrollTo и scrollIntoView

Оба метода принимают опцию behavior. Значение smooth включает анимацию, даже если в CSS ничего не задано:

// кнопка возврата наверх
document.querySelector('.to-top').addEventListener('click', () => {
  window.scrollTo({ top: 0, behavior: 'smooth' });
});

// прокрутить к конкретному блоку
document.getElementById('price').scrollIntoView({ behavior: 'smooth', block: 'start' });

Саму кнопку удобнее делать тегом button, а не ссылкой: варианты разметки есть в инструкции как сделать кнопку в HTML. Полный список опций есть в справке MDN по window.scrollTo() и Element.scrollIntoView().

У behavior есть третье значение, auto. Оно стоит по умолчанию и берёт поведение из CSS: при html { scroll-behavior: smooth; } обычный scrollTo тоже станет плавным.

Свой скролл с заданной длительностью

Если нужна конкретная скорость, например ровно 600 мс, анимацию пишут сами через requestAnimationFrame:

function smoothScrollTo(targetY, duration) {
  const startY = window.scrollY;
  const distance = targetY - startY;
  const startTime = performance.now();
  const ease = t => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2);

  function step(now) {
    const progress = Math.min((now - startTime) / duration, 1);
    window.scrollTo(0, startY + distance * ease(progress));
    if (progress < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

smoothScrollTo(3100, 600);

Функция ease медленно разгоняет прокрутку и плавно тормозит у цели. Важная деталь: на html в этом случае не должно стоять scroll-behavior: smooth, иначе каждый промежуточный scrollTo браузер начнёт сглаживать сам, и анимации наложатся.

Такие функции на requestAnimationFrame пишет почти каждый JavaScript-разработчик. Если стрелочные функции и performance.now() пока читаются с трудом, базу языка дают курсы по JavaScript.

Плавная прокрутка колесом мыши

scroll-behavior не трогает прокрутку, которую делает сам человек колесом, тачпадом или клавишами. Эффект «вязкого» скролла, как на сайтах студий, делают библиотеки.

  • Lenis ставится через npm i lenis. По README сглаживание по умолчанию задаёт интерполяция lerp (0,1), а длительность duration действует, только если lerp не задан. Прокрутку к якорям включает опция anchors.
  • Плагин ScrollSmoother из GSAP подойдёт тем, кто уже собирает анимации на этой библиотеке.
  • Старый вариант на jQuery через $('html, body').animate() до сих пор встречается в шаблонах, но для новых проектов хватает нативных методов из блока выше.

Подключать библиотеку ради одного якорного меню не стоит: это лишний код, который замедлит загрузку страницы.

Какой способ выбрать

Колёсико мыши и плавная дуга прокрутки на макете страницы
Задача Способ
меню из якорных ссылок на лендинге html { scroll-behavior: smooth; }
заголовок прячется под шапкой scroll-padding-top или scroll-margin-top
прокрутка внутри блока scroll-behavior на сам контейнер
кнопка возврата наверх, скролл по событию scrollTo или scrollIntoView с behavior: 'smooth'
нужна своя длительность функция на requestAnimationFrame
плавное колесо мыши Lenis, GSAP ScrollSmoother

Для сайта на Tailwind CSS отдельный код не нужен: класс scroll-smooth на теге html задаёт то же свойство, это описано в документации Tailwind.

Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 700 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Почему не работает плавный скролл

Большинство проблем сводится к пяти причинам.

  1. Свойство стоит на body, а не на html. MDN прямо пишет, что с body оно на окно не переносится. Решение: перенести правило на html или :root.
  2. Прокручивается не страница, а обёртка. Если контент лежит в блоке с overflow: auto и фиксированной высотой, крутится этот блок. Решение: поставить scroll-behavior на него.
  3. В системе включено уменьшение анимации. Если вы обернули правило в prefers-reduced-motion, у вас самих прокрутка будет мгновенной. Это не баг, проверьте настройку доступности.
  4. Ссылка ведёт не туда. href="#Price" и id="price" не совпадут, регистр букв важен. Скопируйте id из разметки, а не набирайте заново.
  5. Скрипт перехватывает клик. Обработчик с event.preventDefault() на ссылках отменяет обычный переход по якорю, и CSS уже не участвует.

Обратная ситуация тоже бывает. В одностраничном приложении после смены экрана страница медленно едет наверх, хотя должна сразу показаться сверху. Решение: вызвать window.scrollTo({ top: 0, behavior: 'instant' }), значение instant отменяет плавность из CSS.

Поддержка браузерами

Свойство поддерживают все современные браузеры. Последним поддержку добавил Safari: до версии 15.4 он scroll-behavior игнорировал, и якорь просто срабатывал рывком. Ничего не ломается, поэтому отдельный запасной вариант не нужен.

Браузер scroll-behavior scroll-margin-top
Chrome с версии 61 с версии 69
Edge с версии 79 с версии 79
Firefox с версии 36 с версии 68
Safari с версии 15.4 с версии 14.1

Версии взяты из таблиц совместимости MDN, актуальный охват по странам удобно смотреть на Can I use. Сам механизм плавной прокрутки описан в спецификации CSSOM View.

Где освоить CSS системно

Плавный скролл решается одной строкой, но вокруг неё десятки похожих задач: сетки, адаптивность, анимации, доступность. Если вы хотите верстать без поиска каждого свойства, имеет смысл пройти курс по CSS с практикой на реальных макетах.

Посмотреть, чем занимается HTML-верстальщик и как стать фронтенд-разработчиком, можно в наших разборах профессий. Все программы по навыку собраны в каталоге курсов по CSS.

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
HTML и CSS с нуля
Перейти на сайт курса
SkillboxSkillbox91 626 ₽7635 ₽/мес.11 месяцевОбзор курса
HTML и CSS. Адаптивная вёрстка и автоматизация
Перейти на сайт курса
HTMLAcademyHTML Academy23 400 ₽3900 ₽/мес.2 месяцаОбзор курса
Основы вёрстки HTML и CSS
Перейти на сайт курса
Академия СинергияАкадемия Синергия32 676 ₽2723 ₽/мес.3 месяцаОбзор курса
HTML и CSS с нуляSkyproSkypro195 160 ₽7972 ₽/мес.12 месяцевОбзор курса
Онлайн-курс Вёрстка на HTML и CSS
Перейти на сайт курса
БруноямБруноям29 900 ₽2491 ₽/мес.3 месяцаОбзор курса

Больше программ — в полном каталоге курсов по CSS

Часто задаваемые вопросы

Как сделать плавный скролл на CSS одной строкой?

Добавьте в стили правило html { scroll-behavior: smooth; }. После этого переходы по якорным ссылкам на странице станут плавными без JavaScript.

Почему scroll-behavior: smooth не работает?

Чаще всего свойство стоит на body, а не на html, или прокручивается не страница, а блок-обёртка с overflow: auto. Ещё одна причина: в системе включено уменьшение анимации, а правило обёрнуто в prefers-reduced-motion.

Можно ли изменить скорость плавной прокрутки в CSS?

Нет. Длительность и кривую анимации при scroll-behavior: smooth выбирает браузер. Если нужна своя скорость, пишут функцию на JavaScript через requestAnimationFrame или подключают библиотеку вроде Lenis.

Как сделать отступ при прокрутке к якорю под фиксированную шапку?

Задайте scroll-padding-top на html с высотой шапки, например 80px. Если отступ нужен только у отдельных блоков, используйте scroll-margin-top на самих блоках.

Как сделать плавную прокрутку наверх по кнопке?

В обработчике клика вызовите window.scrollTo({ top: 0, behavior: 'smooth' }). Опция behavior: 'smooth' работает даже без правила в CSS.

Работает ли scroll-behavior в Safari?

Да, с версии 15.4. В более старых версиях свойство игнорируется, и переход по якорю просто происходит мгновенно, без ошибок.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!