Перейти к содержимому
Забрать бесплатно
Модификации выключены. Так эта страница выглядит на обычной Тильде.

Бегущая строка в Тильде: как сделать и где рвётся шов

29 июля 2026 9 МИН КОМАНДА MODKOD

Бегущей строки в Тильде нет — ни блока, ни тега, ни класса: во всём рантайме два десятка бесконечных CSS-анимаций, и ни одна не двигает содержимое по горизонтали. Значит строку пишут руками, и почти все пишут её с одной и той же ошибкой: шов между копиями не сходится, и раз в двадцать секунд строка дёргается. Разберём живой пример, найдём в нём этот шов и заодно правило самой Тильды, которое молча отключает анимацию — но только на широких экранах.

✓ ПРОВЕРЕНО 29 июля 2026 искал бегущую строку по 114 файлам JS, 37 CSS и 22 сохранённым страницам: `<marquee>` — 0, `t-ticker`/`runline`/`running-line`/`scrolling-text` — 0, `marquee` без префикса `-webkit-` — 0; выписаны все 20 бесконечных CSS-анимаций рантайма с периодами; рукописный тикер снят с трёх живых страниц дословно и посчитан по элементам; правило `transform:none!important` найдено и проверено на вложенность в media-запрос подсчётом скобок — рантайм Тильды из 114 файлов JS и 37 файлов CSS, плюс 22 сохранённые страницы

«Бегущая строка тильда» спрашивают часто, а в самой Тильде такого блока нет. Проверим это, разберём единственный живой пример, который у нас есть, найдём в нём ошибку — и покажем правило самой Тильды, из-за которого рукописная строка иногда стоит на десктопе и едет на телефоне.

Короткий ответ

Штатного блока нет: <marquee> — 0, t-ticker, runline, running-line, scrolling-text — по 0 во всём рантайме и во всех сохранённых страницах. Пишут руками, на overflow:hidden плюс transform в бесконечной анимации. Главная ошибка — шов: при translateX(-50%) вторая половина списка должна быть попиксельно равна первой, а её собирают на глаз.

Чего в Тильде нет — с числами

что искалJS (114 файлов)CSS (37)страницы (22)
<marquee000
marquee без -webkit-000
t-ticker000
runline, running-line000
scrolling-text000
animation-play-state003
prefers-reduced-motion0022

Две последние строки требуют оговорки, и она важнее самих чисел.

Три вхождения animation-play-state — это один и тот же рукописный тикер, скопированный в три страницы. В коде Тильды его нет ни разу.

А 22 вхождения prefers-reduced-motion — наши собственные. Все до одного лежат в макетах и в сниппетах модификаций из этого же репозитория. У Тильды по всем 151 файлу JS и CSS — ноль, при контрольных 1732 вхождениях @media|addEventListener в тех же файлах. Это ровно та ловушка, в которую легко попасть, когда считаешь «по проекту»: свой текст попадает в выборку и подтверждает то, что ты хотел увидеть.

Ловушка: marquee в Тильде всё-таки встречается

Десять раз, в пяти сохранённых страницах. И ни одно вхождение не про бегущую строку:

@supports (overflow:-webkit-marquee) and (justify-content:inherit) {
  #rec721455217 .t-menu__link-item,
  #rec721455217 .t-menu__link-item.t-active { opacity: 1 !important; }
}

Это проба на движок: overflow:-webkit-marquee вместе с justify-content:inherit принимает не всякий браузер, и Тильда пользуется этой парой как определителем, чтобы поправить прозрачность пунктов меню. Тело правила про меню, а не про движение. Само свойство здесь только повод, к бегущей строке оно отношения не имеет.

Так что если считать marquee по подстроке в нижнем регистре — получится уверенное «в Тильде есть marquee», и это будет неправдой. Отбрасывать нужно отрицательным просмотром назад: (?<!-webkit-)marquee. С ним — ноль.

Двадцать бесконечных анимаций, и ни одной бегущей строки

Полная перепись animation: … infinite по рантайму:

анимацияпериодчто это
t706_countdown4 скольцо вокруг цифры «товар удалён»
tStorePreloaderFlickerAnimation1.5 смерцание заглушки товара
tFeedPreloaderFlickerAnimation1.5 сто же в ленте
store-horizontal-scroll-icon1.5 сподсказка «ряд прокручивается»
t-submit-spinner0.6 и 0.7 сспиннер в кнопке формы
t776__icon-anim1.5 сиконка в блоке T776
t-arrow-bottom1.7 сстрелка вниз на обложке
spin2 сспиннер доставки
lds-ellipsis1/2/30.6 строеточие-загрузка доставки

Все двадцать — индикаторы: «идёт загрузка», «сейчас исчезнет», «здесь можно прокрутить». Ни одна не перемещает содержимое.

Ближе всех к бегущей строке подошла подсказка прокрутки в каталоге:

.t-store__scroll-icon {
  animation: store-horizontal-scroll-icon 1.5s infinite
}
@keyframes store-horizontal-scroll-icon {
  0%  { opacity: 0; transform: translate3d(-20px, 0, 0) }
  10% { opacity: 0; transform: translate3d(-20px, 0, 0) }
  20% { opacity: 1; transform: translate3d(-…

Но едет здесь иконка, а не товары. Тильда подсказывает жест, а не двигает контент.

Живой пример: что люди вставляют на самом деле

Вот рукописный тикер с живой страницы — целиком, включая обёртку записи. Он встретился в трёх сохранённых страницах, и во всех трёх это одна и та же запись rec2222269321:

<div id="rec2222269321" class="r t-rec" style=" "
     data-animationappear="off" data-record-type="131">
  <!-- nominify begin -->
  <style>
    .ticker { width:100%; overflow:hidden; background:#f8f3e9;
              padding:14px 0; white-space:nowrap; box-sizing:border-box; }
    .ticker__wrapper { display:inline-flex;
                       animation: scroll 20s linear infinite; }
    .ticker__item { padding-right:60px; font-family:Arial, sans-serif;
                    font-size:18px; font-weight:500; color:#2a2a2a;
                    letter-spacing:0.3px; }
    @keyframes scroll { 0% { transform: translateX(0); }
                        100% { transform: translateX(-50%); } }
    .ticker:hover .ticker__wrapper { animation-play-state: paused; }
    @media (max-width: 768px) {
      .ticker__item { font-size:14px; padding-right:40px; } }
  </style>
  <div class="ticker">
    <div class="ticker__wrapper">
      <span class="ticker__item">Бесплатная доставка от 2000 рублей</span>
      <span class="ticker__item">🚚 Бесплатная доставка от 2000 рублей</span>
      <!-- Дублируем второй раз для бесшовной анимации -->
      <span class="ticker__item">🚚 Бесплатная доставка от 2000 рублей</span>
      …

Устроено правильно: overflow:hidden на рамке, inline-flex внутри, движение через transform, а не через left или margin. Три вещи здесь стоит отметить отдельно.

<!-- nominify begin --> — это не украшение. Без него Тильда минифицирует вставленный код, и @keyframes со <style> может пострадать.

data-animationappear="off" в самой записи — автор отключил анимацию появления. Ниже станет понятно, что это не перестраховка, а необходимость.

animation-play-state: paused по :hover — приятно на десктопе и не существует на телефоне. У самой Тильды это свойство не встречается ни разу.

Находка: шов не сходится

Автор написал в комментарии «Дублируем второй раз для бесшовной анимации», и это верная мысль: при translateX(-50%) содержимое обязано быть двумя одинаковыми половинами, иначе на стыке будет рывок. Посчитаем, что получилось на самом деле:

  • span.ticker__item — 6;
  • уникальных текстов — 2;
  • у элементов 2–6 в начале стоит 🚚, у первого его нет.

Шесть элементов, сдвиг на половину — шов приходится на четвёртый. Значит четвёртый должен быть попиксельно таким же, как первый. Четвёртый — с эмодзи, первый — без. Раз в двадцать секунд строка дёргается.

И это ещё не всё. -50% считается от ширины всей обёртки, а элементы разной ширины: первый уже остальных на ширину эмодзи с пробелом. Половина ширины шести элементов при таком наборе не равна ширине трёх — то есть сдвиг не попадает даже на границу элемента. Дефект двойной: не совпадает содержимое и не совпадает геометрия.

Правило простое: дублируется весь набор целиком, и половины должны быть идентичны — и по составу, и по вёрстке. Пять «с грузовиком» и один «без» — это не два набора по три.

Правило Тильды, которое гасит анимацию — но только на десктопе

В tilda-animation-2.0.min.css есть такое:

@media screen and (min-width: 980px) {
  …
  .t-animate_started[data-animate-distance],
  .t-animate_started[data-animate-scale] { transform: none !important }
}

Проверено на вложенность подсчётом скобок: между открывающим @media и этим правилом 18 открывающих { и 17 закрывающих } — media-блок ещё не закрыт, и правило действительно последнее в нём.

Логика Тильды понятна: после того как элемент появился, сдвиг и масштаб обнуляются, чтобы не мешали. Но !important на transform бьёт по всему, что внутри и что двигается трансформом, — в том числе по вашей бегущей строке. А поскольку правило заперто в min-width:980px, на телефоне его нет.

Отсюда самый неприятный вид жалобы: «на компьютере не едет, на телефоне едет». Причина не в браузере и не в железе — в media-запросе. Лечение — то самое, что автор живого примера уже применил: data-animationappear="off" у записи, или вынести строку из блока с анимацией появления.

Соседнее правило из того же файла стоит знать заодно:

.t-animate-for-wrapper .t-animate:not(.t-animate_started) {
  opacity: 0 !important; transition-duration: 0s !important
}

Пока класс t-animate_started не поставлен, содержимое невидимо и жёстко. Если ваш скрипт зависит от размеров элемента, который ещё не «появился», он их получит — но нулевую прозрачность не переопределит ничем, кроме своего !important.

Что из этого делать

Дублируйте набор целиком и считайте элементы. Чётное число, две половины идентичны — иначе шов будет виден. Проверяется без глаз: количество элементов делится на два, и элемент №1 совпадает с элементом №(n/2 + 1) посимвольно.

Задавайте размеры картинкам. Для строки из логотипов это главное: пока картинки грузятся, ширина обёртки растёт, и -50% каждый раз означает другое расстояние. Ширина и высота в разметке или в CSS снимают проблему целиком.

Не рассчитывайте на :hover. На телефоне его нет. Если пауза нужна — ставьте её по касанию или не ставьте вовсе.

Уважайте prefers-reduced-motion. Тильда этого не делает нигде, но это две строки:

@media (prefers-reduced-motion: reduce) {
  .ticker__wrapper { animation: none }
}

Держите строку подальше от анимации появления. Либо data-animationappear="off" у записи, либо строка живёт в записи без анимации. Иначе получите поведение, которое выглядит случайным, а на самом деле зависит от ширины окна.

Откуда числа

Поиск по 114 файлам JS, 37 файлам CSS и 22 сохранённым страницам, счёт по вхождениям, а не по строкам: минифицированный файл — это одна строка, и grep -c на нём всегда вернёт 1. Контроли печатались рядом с результатом: t_onFuncLoad — 271 по JS, data-record-type — 219 по страницам, t-animate — 39 в файле анимаций.

Перепись бесконечных анимаций собрана регулярным выражением animation:\s*([\w-]+)\s+([\d.]+m?s)[^;}]*infinite по всем файлам CSS; в таблице приведены уникальные имена, в выдаче каждое встречается дважды — по одному разу в каждой из двух копий файла в корпусе.

Тикер снят из tildaJS/source/test1234.html и найден в том же виде в modkit/stand/form.html и modkit/stand/shop.html; элементы посчитаны регулярным выражением по <span class="ticker__item">, тексты сравнены как строки.

И общая оговорка. Отсутствие имени в наборе файлов не равно отсутствию его в Тильде: у Тильды отложенная привязка через t_onFuncLoad, и код блока может лежать в постраничном бандле, которого у нас нет. Для бегущей строки это означает: утверждать «Тильда никогда не добавит такой блок» нельзя, а утверждать «в разобранном рантайме его нет» — можно, и это проверяется одним поиском.

Вопросы по этой статье 4

Нет. Ни тега `<marquee>`, ни класса `t-ticker`, `runline`, `running-line` или `scrolling-text` — по нулю во всех 114 файлах JS, 37 файлах CSS и 22 сохранённых страницах. Бесконечных CSS-анимаций в рантайме двадцать, и все двадцать — спиннеры, прелоадеры, кольцо отсчёта и подпрыгивающая стрелка; ни одна не двигает содержимое по горизонтали.