Как добавить кнопку «наверх» в Django-сайт и Django Admin: настройка, CSP, доступность, мобильная версия и работа рядом с cookie-баннерами.— Читать дальше «Кнопка «наверх» в Django: почему в проде это уже не три строки JavaScript»
Как сделать scroll-to-top для сайта и Django Admin, не забыв про мобильные устройства, CSP, доступность, плавающие виджеты и нормальную настройку без правки шаблонов.
На первый взгляд кнопка «наверх» — задача на пять минут. Добавил position: fixed, обработчик window.scrollTo() — готово.
Но стоит этой кнопке появиться в живом проекте, как выясняется, что она пересекается с cookie-баннером, мешает чату поддержки, выглядит иначе в мобильной версии, не дружит со строгим CSP или пропадает из Django Admin.
В итоге маленькая UI-деталь начинает обрастать условиями. Я решил собрать их в отдельный Django-пакет — django-scroll-to-top
Для небольшого сайта, где нет сложной верстки, админки, CSP и требований к повторному использованию, самый простой вариант действительно выглядит примерно так:
<button id="back-to-top" type="button">↑</button>
<script>
document.querySelector("#back-to-top").addEventListener("click", () => {
window.scrollTo({ top: 0, behavior: "smooth" });
});
</script>
Это нормальное решение. Не всегда стоит тянуть пакет ради одной кнопки.
Но в реальном Django-проекте быстро появляются дополнительные вопросы:
Именно в этот момент «три строки JavaScript» превращаются в отдельный компонент.
Цель была не в том, чтобы сделать ещё одну стрелку в правом нижнем углу. Хотелось собрать переиспользуемый компонент со следующими свойствами:
Пакет в итоге хранит обычные настройки установки в settings.py, а визуальное поведение — в базе данных. Это позволяет менять кнопку через Django Admin, публиковать новую версию настроек и при необходимости откатываться на предыдущую. В проекте есть отдельные профили для публичного сайта и Django Admin, а ревизии могут быть черновыми, опубликованными или архивными.
Базовый сценарий начинается с установки:
python -m pip install django-scroll-to-top
python manage.py migrate
В settings.py добавляем приложение. Если нужна поддержка стандартной админки, пакет должен идти раньше django.contrib.admin:
INSTALLED_APPS = [
"django_scroll_to_top",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
Включаем области, где должна работать кнопка:
DJANGO_SCROLL_TO_TOP = {
"SITE_ENABLED": True,
"ADMIN_ENABLED": True,
}
Для публичной части добавляем URLConf пакета:
from django.urls import include, path
urlpatterns = [
path(
"scroll-to-top/",
include(
("django_scroll_to_top.urls", "django_scroll_to_top"),
namespace="django_scroll_to_top",
),
),
]
А в общий шаблон сайта — один тег:
{% load scroll_to_top %}
<!doctype html>
<html lang="ru">
<head>
...
</head>
<body>
{% block content %}{% endblock %}
{% scroll_to_top %}
</body>
</html>
На стандартном Django Admin ничего дополнительно вставлять не нужно: пакет использует обычный механизм разрешения шаблонов Django. Если же в проекте переопределён admin/base_site.html тег можно добавить вручную в блок footer
{% extends "admin/base.html" %}
{% load scroll_to_top %}
{% block footer %}
{{ block.super }}
{% scroll_to_top scope="admin" %}
{% endblock %}
Мне не хотелось хранить в базе шаблоны, произвольный CSS или JavaScript. Это неудобно для сопровождения и создаёт лишнюю поверхность для ошибок.
Поэтому визуальная часть собрана из контролируемых вариантов:
Нижний правый угол страницы редко бывает свободен. Там часто живут:
Пакет умеет рассматривать такие элементы как препятствия. Для этого можно пометить элемент атрибутом:
<div data-scroll-top-obstacle>
<!-- Cookie banner, chat launcher или другой фиксированный элемент -->
</div>
Дальше для кнопки можно выбрать поведение: игнорировать препятствия, сдвинуться вдоль края, попробовать другой угол или скрыться, если безопасного места не осталось.
Для сложных виджетов есть отдельный адаптер: он может отслеживать появление и исчезновение элементов, например компактного launcher после закрытия cookie-баннера. При этом ни cookie-пакет, ни чат не становятся зависимостями django-scroll-to-top
У кнопки есть понятное имя для screen reader, поддержка клавиатуры, видимый focus-visible, минимальный размер области нажатия и режим prefers-reduced-motion.
Если пользователь отключил анимации на уровне системы, плавная прокрутка не будет навязываться. Если JavaScript не загрузился, кнопка остаётся обычной ссылкой на начало документа.
Полный независимый аудит WCAG 2.2 AA и тестирование масштабирования 200% и 400% пока находятся в roadmap, поэтому называть компонент полностью сертифицированным по WCAG было бы неправильно. Но структурные требования — клавиатурная доступность, фокус, reduced motion, forced-colors и безопасная работа без JavaScript — уже заложены в компонент и покрываются тестами.
В корпоративных проектах часто нельзя просто добавить inline-скрипт и включить unsafe-inline ради одной кнопки.
По умолчанию компонент использует same-origin CSS и JavaScript. Для него подходит политика такого вида:
Content-Security-Policy:
default-src 'self';
style-src 'self';
script-src 'self';
Настраиваемые цвета и размеры отдаются не через inline-стили, а через версионированный stylesheet endpoint. Это позволяет сохранить простой контракт с одним template-тегом и не ослаблять CSP.
Отдельно пришлось подумать о загружаемых SVG. Админ не рендерит исходный файл как есть: SVG проходит санитарную обработку. Скрипты, обработчики событий, внешние ресурсы, встроенные документы и небезопасные namespace отклоняются. Для загружаемых иконок также хранится информация об авторе, источнике и лицензии.
Одна из самых полезных вещей в пакете — не сама кнопка, а жизненный цикл её настроек.
Можно создать черновик, посмотреть результат в live preview, опубликовать изменения или вернуться к предыдущей версии. Это особенно удобно, когда кнопку настраивает не разработчик, а контент-менеджер или дизайнер.
У ревизий есть три состояния:
django-scroll-to-top имеет смысл, когда кнопка нужна в нескольких проектах, должна работать в Django Admin, настраиваться без деплоя или жить в окружении со строгими требованиями к CSP и интерфейсу.
Для лендинга на одну страницу проще и правильнее написать несколько строк самостоятельно. Это будет быстрее, понятнее и дешевле в сопровождении.
Но если такая маленькая деталь начинает повторяться в нескольких продуктах, появляется необходимость поддерживать мобильную версию, доступность, независимые настройки для сайтов и админки, то отдельный компонент уже перестаёт быть избыточным.
Сейчас пакет выпущен как beta-версия 0.2.0, требует Python 3.10+ и поддерживает Django 4.2 LTS, 5.x и 6.0. Лицензия — MIT.
Исходный код, документация и примеры использования доступны в GitHub-репозитории проекта.
Пакет опубликован в PyPI под именем django-scroll-to-top.
Обратная связь, баг-репорты и предложения по интеграции с кастомными Django Admin-темами приветствуются в Issues.
| # | Наименование новости | Тональность | Информативность | Дата публикации |
|---|---|---|---|---|
| 1 | Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design | 5 | 7 | 02-07-2026 |
| 2 | Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках | 0 | 7 | 25-06-2026 |
| 3 | Как ограничить расходы на OpenAI API, чтобы ИИ-агенты не сожгли бюджет | 0 | 8 | 04-07-2026 |
| 4 | Аварийное восстановление до аварии: как настроить DRaaS, пока ничего не упал | 5 | 8 | 02-07-2026 |
| 5 | Deckhouse Conf 2026: зачем инженерам самописный SDN и виртуалки в Kubernetes | 0 | 5 | 02-07-2026 |
| 6 | Нейросеть для генерации изображений: ТОП-7 ИИ генераторов картинок в 2026 | 0 | 7 | 03-07-2026 |
| 7 | Как оживить фото нейросетью бесплатно: пошаговый гайд 2026 года | 5 | 7 | 03-07-2026 |
| 8 | Нейросеть для генерации видео: ТОП-6 ИИ для создания видео в 2026 году | 0 | 8 | 03-07-2026 |
| 9 | AI-агенты сломали управление жизненным циклом идентификации. Что с этим делать | 0 | 7 | 04-07-2026 |
| 10 | Как мы нашли баг в HTTP-библиотеке hyper | 0 | 5 | 25-06-2026 |