Использование вкладышей

djLint включает в себя множество правил для проверки стиля и валидности ваших шаблонов. Используйте все преимущества линтера, настроив его на использование предустановленного профиля для выбранного вами языка шаблонов.

djlint /path/to/templates --lint

# with custom extensions
djlint /path/to/templates -e html.dj --profile=django

# or to file
djlint /path/to/this.html.j2  --profile=jinja

Включение или отключение правил

Большинство правил включены по умолчанию. Правила могут быть отключены в командной строке с помощью флага --ignore. Правила могут быть включены с помощью флага --include.

Например:

djlint . --lint --include=H006,H017 --ignore=H013,H015

Это также можно сделать через Конфигурация файл.

Правила

Код Значение По умолчанию
T001 Переменные должны быть заключены в пробел. Например: {{ this }} ✔️
T002 В тегах следует использовать двойные кавычки. Ex {% extends "this.html" %} ✔️
T003 Конечный блок должен иметь имя. Например: {% endblock body %}. -
D004 (Django) Статические урлы должны следовать шаблону {% static path/to/file %}. ✔️
J004 (Jinja) Статические урлы должны следовать шаблону {{ url_for('static'...)}}. ✔️
H005 Html-тег должен иметь непустой атрибут lang. ✔️
H006 Тег img должен иметь атрибуты height и width. -
H007 <!DOCTYPE ... > должен присутствовать перед тегом html. ✔️
H008 Атрибуты должны быть заключены в двойные кавычки. ✔️
H009 Имена тегов должны быть в нижнем регистре. ✔️
H010 Имена атрибутов должны быть в нижнем регистре. ✔️
H011 Значения атрибутов должны быть заключены в кавычки. ✔️
H012 Вокруг атрибута = не должно быть пробелов. ✔️
H013 Тег img должен иметь атрибуты alt. ✔️
H014 Больше пустых строк, чем сохраняет конфигурация. ✔️
H015 После тегов h следует перевод строки. ✔️
H016 Отсутствие тега title в html. ✔️
H017 Пустые теги должны быть самозакрывающимися (противоречит правилу H018). -
D018 (Django) Внутренние ссылки должны использовать шаблон {% url ... %}. ✔️
H018 Пустые теги по своей природе являются самозакрывающимися и должны заканчиваться символом «>», а не «/>» (конфликт с: H017). -
J018 (Jinja) Внутренние ссылки должны использовать шаблон {% url ... %}. ✔️
H019 Замените javascript:abc() на событие on_ и реальный url. ✔️
H020 Найдена пустая пара тегов. Рассмотрите возможность удаления. ✔️
H021 Следует избегать инлайн-стилей. ✔️
H022 Используйте HTTPS для внешних ссылок. ✔️
H023 Не используйте ссылки на сущности. ✔️
H024 Опускайте тип в скриптах и стилях. ✔️
H025 Тег кажется бесхозным. ✔️
H026 Пустые теги id и class могут быть удалены. ✔️
T027 В синтаксисе шаблона найдена незакрытая строка. ✔️
T028 Рассмотрите возможность использования тегов без пробелов внутри значений атрибутов. {%- if/for -%} -
H029 Рассмотрите возможность использования строчных значений метода формы. ✔️
H030 Рассмотрите возможность добавления мета-описания. ✔️
T032 В тегах шаблона обнаружены лишние пробелы. ✔️
H033 В действии формы обнаружен лишний пробел. ✔️
T034 Вы намеревались использовать {% … %} вместо {% … }%? ✔️
H036 Избегайте использования тегов br. ✔️
H037 Найдено дублирование атрибута. ✔️
T038 Блочный тег не имеет соответствующего закрывающего тега. ✔️
T039 Найден незакрытый тег шаблона. ✔️
T040 Отсутствующее или пустое имя шаблона в теге extends или include. ✔️
H041 Тег закрывается в другом блоке шаблона, чем тот, в котором он был открыт. ✔️
T041 Тег extends должен быть первым тегом в шаблоне. ✔️
H042 Атрибут for у label не имеет соответствующего id в этом файле. ✔️
T042 Содержимое вне блока не рендерится в шаблоне, который расширяет другой. ✔️
H043 Тег button должен иметь атрибут type. ✔️
T043 Имя блока используется в шаблоне более одного раза. ✔️
H044 В thead не следует смешивать ячейки th и td. ✔️
T044 Тег вывода содержит ключевое слово инструкции; используйте блочный тег. ✔️
H045 Тег iframe должен иметь атрибут title. ✔️
T045 Тег шаблона внутри html-комментария всё равно выполняется; чтобы его отключить, используйте комментарий шаблона. ✔️
H046 Значение tabindex не должно быть положительным. ✔️
H047 aria-hidden не следует ставить на фокусируемый элемент. ✔️
H048 Такого aria-атрибута нет в спецификации. ✔️
H049 Viewport не должен запрещать масштабирование страницы. ✔️
H050 Элемент устарел, его следует заменить. ✔️
H051 Роль не входит в те, что ARIA определяет для разметки. ✔️
H052 Meta refresh не должен перезагружать или перенаправлять страницу по таймеру. ✔️
H053 Id используется в файле более одного раза. ✔️
H054 Интерактивный элемент не следует вкладывать в другой. ✔️
H055 Атрибут lang должен быть языковым тегом, например en или pt-BR. ✔️
H056 Src не должен быть пустым. ✔️
H057 Video должен иметь дорожку субтитров. ✔️

Кодовые шаблоны

Первая буква кода соответствует схеме:

  • D: применяется специально для Django
  • H: применяется к html
  • J: применяется специально для Jinja
  • M: применяется специально для Handlebars
  • N: применяется специально для Nunjucks
  • T: применяется в целом к шаблонам

Подробное описание правил

T001

Переменные должны быть заключены в пробел. Например: {{ this }}

Синтаксис шаблона вроде {{user.name}} без внутренних пробелов труднее читать и сравнивать в diff, а непоследовательные пробелы по кодовой базе делают рефакторинг через grep (поиск переменной или тега) ненадёжным: одно и то же выражение существует в нескольких написаниях. Руководства по стилю и Django, и Jinja пишут {{ var }} и {% tag %} с одиночными пробелами.

Не применяется к профилям handlebars и golang.

Неправильно:

{{user.name}}

Правильно:

{{ user.name }}

T002

В тегах следует использовать двойные кавычки. Ex {% extends "this.html" %}

Смешение одинарных и двойных кавычек в тегах шаблона ({% extends %}, {% include %}, {% with %}, {% trans %}, {% now %}) приводит к тому, что одно и то же имя шаблона встречается в двух написаниях, и поиск или массовое переименование пропускает половину вхождений. Стандартизация на двойных кавычках делает аргументы тегов согласованными с кавычками HTML-атрибутов в остальной части файла.

Одинарные кавычки внутри значений HTML-атрибутов (например, <span title="{% trans 'x' %}">) не помечаются, так как двойные кавычки атрибута вынуждают использовать там одинарные.

При quote_style = "single" правило разворачивается и требует одинарные кавычки, и форматировщик пишет их же.

--reformat переписывает эти кавычки за вас, так что срабатывание никогда не требует ручной правки.

Неправильно:

{% extends 'base.html' %}

Правильно:

{% extends "base.html" %}

T003

Конечный блок должен иметь имя. Например: {% endblock body %}.

Когда {% block %} занимает много строк или блоки вложены, безымянный {% endblock %} не даёт понять, какой блок он закрывает, поэтому при правках легко закрыть не тот блок, и дочерние шаблоны переопределят не то содержимое. Имя у endblock документирует парность и позволяет как djLint, так и Django (который выбрасывает TemplateSyntaxError при несовпадающем имени endblock) поймать блок, закрытый не в том месте. Ошибки парности (незакрытые блоки, осиротевшие endblock и несовпадающие имена) проверяются правилом T038.

Отключено по умолчанию; включается флагом --include=T003. --name-endblocks сам записывает имя, так что срабатывание никогда не требует ручной правки.

Имя не требуется, когда блок открывается и закрывается на одной строке, например {% block title %}``{% endblock %}.

Неправильно:

{% block content %}
<p>hello</p>
{% endblock %}

Правильно:

{% block content %}
<p>hello</p>
{% endblock content %}

D004

(Django) Статические урлы должны следовать шаблону {% static path/to/file %}.

Жёстко прописанные пути /static/ обходят тег Django {% static %}: шаблоны ломаются при изменении STATIC_URL (например, при переносе статики на CDN или развёртывании по под-пути) и никогда не подхватывают хэшированные имена файлов из ManifestStaticFilesStorage, что приводит к ошибкам 404 или устаревшим закэшированным ресурсам в продакшене. Правило ищет буквальный префикс /static/, поэтому проект, отдающий статику по другому пути, им не покрывается.

Неправильно:

<link rel="stylesheet" href="/static/css/style.css">

Правильно:

<link rel="stylesheet" href="{% static 'css/style.css' %}">

J004

(Jinja) Статические урлы должны следовать шаблону {{ url_for('static'...)}}.

Жёстко прописанные пути /static/ обходят url_for(‘static’, …) из Flask/Jinja: при монтировании приложения под URL-префиксом или изменении каталога/хоста статики ресурсы отдают 404, а добавляемые фреймворком query-строки для сброса кэша теряются. Правило ищет буквальный префикс /static/, поэтому проект, отдающий статику по другому пути, им не покрывается.

Неправильно:

<link rel="stylesheet" href="/static/css/style.css">

Правильно:

<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">

H005

Html-тег должен иметь непустой атрибут lang.

Без атрибута lang на <html> скринридеры угадывают правила произношения и могут читать страницу не на том языке, а браузеры не могут корректно предложить перевод, расстановку переносов или локальные кавычки. Объявление языка страницы является критерием успеха 3.1.1 WCAG 2.1 (уровень A).

lang="" и lang без значения одинаково означают, что язык неизвестен, поэтому они помечаются так же, как отсутствующий атрибут.

Неправильно:

<!DOCTYPE html>
<html>
</html>

Правильно:

<!DOCTYPE html>
<html lang="en">
</html>

H006

Тег img должен иметь атрибуты height и width.

Отключено по умолчанию; включается флагом --include=H006.

Когда у <img> нет width и height, браузер не может зарезервировать место до загрузки изображения, и окружающий контент прыгает по мере загрузки картинок. Этот сдвиг вёрстки ухудшает Cumulative Layout Shift (метрику Core Web Vitals) и может приводить к промахам по кликам, пока страница не стабилизировалась.

Неправильно:

<img src="cat.png" alt="Cat">

Правильно:

<img src="cat.png" alt="Cat" width="120" height="80">

H007

<!DOCTYPE ... > должен присутствовать перед тегом html.

Без <!DOCTYPE> перед тегом <html> браузеры рендерят страницу в режиме совместимости (quirks mode), эмулируя устаревшую блочную модель и поведение вёрстки, поэтому CSS отображается в браузерах по-разному. Теги шаблона и комментарии перед doctype допустимы; он должен предшествовать только самому тегу <html>.

Неправильно:

<html lang="en">
</html>

Правильно:

<!DOCTYPE html>
<html lang="en">
</html>

H008

Атрибуты должны быть заключены в двойные кавычки.

Смешанные стили кавычек затрудняют чтение и поиск значений атрибутов, а значения в одинарных кавычках ломаются, как только в содержимом появляется апостроф. Двойные кавычки являются соглашением, принятым в спецификациях HTML, форматерах и большинстве руководств по стилю, поэтому стандартизация на них держит шаблоны в согласии с остальной экосистемой.

Неправильно:

<div class='content'></div>

Правильно:

<div class="content"></div>

H009

Имена тегов должны быть в нижнем регистре.

HTML-парсеры принимают имена тегов в верхнем регистре, но сериализации XHTML и XML чувствительны к регистру и отвергают их, а смешанный регистр делает текстовый поиск и ревью diff ненадёжными (grep по <h1> не найдёт <H1>). Имена тегов в нижнем регистре сохраняют переносимость и единообразие шаблонов.

Неправильно:

<H1>Welcome</H1>

Правильно:

<h1>Welcome</h1>

H010

Имена атрибутов должны быть в нижнем регистре.

Имена атрибутов в верхнем регистре недопустимы в сериализациях XHTML/XML и ломают текстовый поиск по шаблонам (grep по src= не найдёт SRC=). DOM всё равно нормализует имена HTML-атрибутов к нижнему регистру, поэтому написание в верхнем регистре добавляет разнобой без всякой пользы.

Неправильно:

<img SRC="cat.png" alt="Cat" width="120" height="80">

Правильно:

<img src="cat.png" alt="Cat" width="120" height="80">

H011

Значения атрибутов должны быть заключены в кавычки.

Значение атрибута без кавычек заканчивается на первом пробеле, поэтому значение вроде class=btn primary молча теряет всё после пробела (браузер считает “primary” отдельным булевым атрибутом). Особенно хрупки значения из переменных шаблона: любой отрендеренный пробел, “=” или “>” ломает тег. Кавычки делают границы значения явными и безопасными.

Неправильно:

<div class=test></div>

Правильно:

<div class="test"></div>

H012

Вокруг атрибута = не должно быть пробелов.

С пробелами вокруг “=” тег читается как три отдельных токена и находится в одной правке от развала: перенос строки или обрезка посередине оставляет голый булев атрибут и обрывок текста. Слитная запись name=“value” является ещё и тем, чего ожидают простые текстовые инструменты (grep, поиск с заменой), поэтому разнобой в пробелах мешает надёжно находить и рефакторить атрибуты.

Неправильно:

<div class = "test"></div>

Правильно:

<div class="test"></div>

H013

Тег img должен иметь атрибуты alt.

Без атрибута alt скринридеры произносят имя файла изображения или вообще ничего, что нарушает WCAG 1.1.1 (Non-text Content). Текст alt является ещё и тем, что видят пользователи, когда изображение не загрузилось. Декоративным изображениям следует давать явно пустой alt=“”, чтобы вспомогательные технологии знали, что их нужно пропустить; это тоже удовлетворяет правилу.

alt без значения равнозначен alt="", то есть декоративному изображению, и принимается.

Неправильно:

<img src="cat.jpg" height="200" width="300">

Правильно:

<img src="cat.jpg" height="200" width="300" alt="A sleeping cat">

H014

Более 2 пустых строк.

Серии пустых строк никак не влияют на отрендеренную страницу (HTML схлопывает пробелы), но раздувают шаблоны и создают шумные diff при изменении соседних строк. Форматер djLint по умолчанию удаляет их полностью (оставляя не более max_blank_lines пустых строк, по умолчанию 0), поэтому оставшиеся серии указывают на неотформатированный код.

Неправильно:

<div>one</div>


<p>two</p>

Правильно:

<div>one</div>

<p>two</p>

H015

После тегов h следует перевод строки.

Заголовки являются блочными ориентирами, задающими структуру документа; когда следующий элемент лепится на ту же строку, что и закрывающий h-тег, эта структура прячется в исходнике, а правки любого из элементов выглядят в diff как изменения обоих. Перевод строки после каждого заголовка держит визуальную структуру шаблона в соответствии со структурой отрендеренного документа.

Неправильно:

<h1>Heading</h1><p>Intro text.</p>

Правильно:

<h1>Heading</h1>
<p>Intro text.</p>

H016

Отсутствие тега title в html.

Спецификация HTML требует элемент title в каждом документе. Без него вкладки браузера, закладки и история показывают голый URL вместо названия страницы, поисковые системы теряют основную метку страницы, а пользователи скринридеров теряют первое, что объявляется при загрузке, что нарушает WCAG 2.4.2 (Page Titled, уровень A).

Срабатывает только на файлах с полным документом <html></html>, поэтому фрагменты и дочерние шаблоны, расширяющие базовый, никогда не помечаются. Оболочкам SPA, задающим заголовок на клиенте, статический <title> всё равно нужен: именно он виден при первой отрисовке, краулерам и при отказе JavaScript.

Неправильно:

<html lang="en">
<body>Content</body>
</html>

Правильно:

<html lang="en">
<head>
<title>My page</title>
</head>
<body>Content</body>
</html>

H017

Пустые теги должны быть самозакрывающимися (противоречит правилу H018).

Шаблоны, которые должны также разбираться как XML/XHTML (или скармливаться XML-инструментам), не принимают пустые (void) элементы без закрывающего слэша, а смешение <br> и <br /> по кодовой базе даёт непоследовательные diff. Это правило вводит соглашение в стиле XHTML, чтобы каждый пустой элемент закрывался одинаково.

Отключено по умолчанию; включается флагом --include=H017. Взаимоисключимо с H018: включайте только одно из двух соглашений.

Неправильно:

<br>
<meta charset="utf-8">

Правильно:

<br />
<meta charset="utf-8" />

D018

(Django) Внутренние ссылки должны использовать шаблон {% url ... %}.

Жёстко прописанные внутренние URL незаметно устаревают, когда путь маршрута меняется в urls.py, порождая битые ссылки и нерабочие action у форм, которые не поймает ни один тест URLconf. {% url %} строит путь по имени маршрута, поэтому переименование пути обновляет все ссылки сразу.

Неправильно:

<a href="/accounts/login">Login</a>

Правильно:

<a href="{% url 'login' %}">Login</a>

H018

Пустые теги по своей природе являются самозакрывающимися и должны заканчиваться символом «>», а не «/>» (конфликт с: H017).

В живом стандарте HTML завершающий слэш у пустого (void) элемента ничего не значит (парсер его игнорирует), поэтому запись <br /> намекает на XML-подобное самозакрытие, которого в HTML нет, и может подтолкнуть читателей ставить слэши у непустых тегов, где лишний / молча отбрасывается и маскирует ошибки с незакрытыми тегами. Это правило требует, чтобы пустые элементы заканчивались простым >.

Отключено по умолчанию; включается флагом --include=H018. Взаимоисключимо с H017: включайте только одно из двух соглашений. SVG <path /> исключён: SVG является XML, и слэш там обязателен.

Неправильно:

<br />
<meta charset="utf-8" />

Правильно:

<br>
<meta charset="utf-8">

J018

(Jinja) Внутренние ссылки должны использовать шаблон {% url ... %}.

Жёстко прописанные внутренние URL незаметно ломаются, когда меняется путь маршрута или приложение монтируется под префиксом, оставляя мёртвые ссылки и формы, отправляющие данные на 404. url_for() строит URL по имени эндпоинта, поэтому изменения маршрутов автоматически распространяются на все шаблоны.

Неправильно:

<a href="/accounts/login">Login</a>

Правильно:

<a href="{{ url_for('login') }}">Login</a>

H019

Замените javascript:abc() на событие on_ и реальный url.

URL вида javascript: ломают клик средней кнопкой и открытие в новой вкладке, не работают при отключённом или не загрузившемся JavaScript, блокируются строгими Content Security Policy и являются классическим каналом XSS-инъекций. Используйте в href настоящий URL, а поведение подключайте обработчиком события. При строгой CSP инлайновые обработчики on* тоже блокируются. Показанный onclick лишь минимальная правка внутри шаблона; предпочтительнее вешать слушатель через addEventListener из файла скрипта.

Неправильно:

<a href="javascript:openPopup()">Open popup</a>

Правильно:

<a href="{% url 'popup' %}" onclick="openPopup(event)">Open popup</a>

H020

Найдена пустая пара тегов. Рассмотрите возможность удаления.

Пустая пара тегов не рендерит контент, но всё равно создаёт DOM-узел, который может подхватить из стилей отступы, рамки или промежутки flex/grid, порождая фантомные зазоры, которые трудно отследить; обычно это остаток разметки от прежней правки. Теги, которые в обычной разметке законно бывают пустыми (td, th, li, dt, dd, slot), исключены. Теги с любым атрибутом (точки монтирования JS вроде <div id="app">``</div>, элементы иконочных шрифтов вроде <i class="fa fa-user">``</i>) тоже не помечаются; правилу соответствуют только совершенно пустые пары без атрибутов.

Неправильно:

<p>Saved.</p>
<span> </span>

Правильно:

<p>Saved.</p>

H021

Следует избегать инлайн-стилей.

Инлайн-стили имеют более высокую специфичность, чем любой селектор в таблице стилей, поэтому переопределять их потом приходится через !important; они блокируются Content Security Policy без ‘unsafe-inline’ в style-src; и они размазывают оформление по шаблонам, так что смена темы или дизайна означает правку разметки вместо одной таблицы стилей. Перенесите объявление в CSS-класс. Одно законное исключение составляют шаблоны HTML-писем: многие почтовые клиенты вырезают блоки <style>, и инлайн-стили там стандартный приём, поэтому исключите каталоги почтовых шаблонов или отключите для них это правило.

Неправильно:

<div style="color: red;">Wrong username or password.</div>

Правильно:

<div class="error">Wrong username or password.</div>

H022

Используйте HTTPS для внешних ссылок.

Обычные http:// подресурсы на странице, отдаваемой по HTTPS, являются смешанным контентом: скрипты, стили и iframe браузеры блокируют сразу, а изображения автоматически апгрейдят или показывают предупреждение. Ссылка <a> на http:// страницу смешанным контентом не является, но всё равно отправляет посетителей по незашифрованному соединению, открытому для перехвата и подмены. Ссылки на внутренние хосты, у которых действительно нет TLS, тоже будут помечены: глушите такие места блоком {# djlint:off H022 #}, а не отключайте правило целиком.

Неправильно:

<a href="http://example.com">Example</a>

Правильно:

<a href="https://example.com">Example</a>

H023

Не используйте ссылки на сущности.

Документы HTML5 используют UTF-8, поэтому литеральный символ работает везде, и именно его реально читают ревьюеры; опечатка в ссылке на сущность (например, &mdsah;) браузером не ловится и выводится как есть, битым текстом. djLint разрешает сущности, несущие синтаксис (&lt;, &gt;, &amp;, &quot;, &apos;, а также фигурные скобки &lbrace; и &rbrace; вместе с &percnt;, &num; и &dollar;, которые в виде литерала сложились бы в разделитель шаблона), и те, что называют невидимый символ, который нельзя прочитать в виде литерала: пробелы (&nbsp;, &thinsp;, &hairsp;), соединители и метки (&zwnj;, &zwj;, &lrm;, &rlm;) и &shy; — в именованной, десятичной и шестнадцатеричной форме.

--reformat сам переписывает сущность в символ, так что срабатывание никогда не требует ручной правки. Сущность внутри тега шаблона относится к тегу, а не к странице, поэтому ни правило, ни форматтер её не трогают.

Неправильно:

<p>Dates 1900 &mdash; 2000</p>

Правильно:

<p>Dates 1900 — 2000</p>

H024

Опускайте тип в скриптах и стилях.

text/javascript и text/css являются значениями по умолчанию в HTML5 для <script> и <style>, так что этот атрибут представляет собой мёртвый груз, который браузер игнорирует; спецификация WHATWG прямо предписывает его опускать. Отказ от него также избавляет от устаревших MIME-строк, ломающих элемент при копировании на модульные скрипты (где type=“module” действительно важен).

Неправильно:

<script type="text/javascript" src="app.js">

Правильно:

<script src="app.js"></script>

H025

Тег кажется бесхозным.

Тег без парного открывающего или закрывающего тега заставляет механизм восстановления после ошибок в браузере угадывать, где кончается элемент, и последующая разметка проглатывается не тем элементом: вёрстка, CSS-селекторы и DOM-запросы JavaScript ломаются молча и по-разному в разных браузерах. H025 также сообщает об <ol> или <ul>, открытом внутри <p>: HTML-парсер закрывает абзац перед списком, поэтому разметка никогда не вкладывается так, как написана.

Неправильно:

<div>
  <p>Hello</p>

Правильно:

<div>
  <p>Hello</p>
</div>

H026

Пустые теги id и class могут быть удалены.

Ни один селектор класса или id не совпадает с пустым атрибутом, а пустой id вдобавок невалидный HTML (значение id не может быть пустой строкой). Селектор наличия вроде div[class] его всё же выбирает, поэтому удаление заметно для стилей, написанных таким образом. Обычно это признак ошибки в шаблоне, где предполагалась подстановка переменной, поэтому удаление или заполнение атрибута не даёт этой ошибке прятаться на виду.

Неправильно:

<div id="" class="">content</div>

Правильно:

<div>content</div>

T027

В синтаксисе шаблона найдена незакрытая строка.

Кавычка, открытая, но не закрытая внутри {% ... %} или {{ ... }}, заставляет шаблонизатор неверно разобрать тег: Django и Jinja либо выбрасывают TemplateSyntaxError при рендеринге, либо молча поглощают остаток аргументов тега как строковое содержимое: страница отвечает ошибкой 500 или рендерится с пропавшими аргументами.

Неправильно:

{% trans "Welcome %}

Правильно:

{% trans "Welcome" %}

T028

Рассмотрите возможность использования тегов без пробелов внутри значений атрибутов. {%- if/for -%}

Отключено по умолчанию; включается флагом --include=T028.

Пробел, который убирает такой тег, это отображаемый пробел, поэтому применяйте приём лишь там, где атрибуту нечего терять. alt="{%- if brand -%}Acme{%- endif -%} logo" выведется как Acmelogo, а svg d="M12 {%- if big -%}20{%- endif -%} 4Z" станет другим контуром. Поэтому правило и отключено по умолчанию.

Теги шаблона внутри значения атрибута выводят в итоговый атрибут окружающие их пробелы и переводы строк, поэтому href или src, собранный обычными тегами {% if %}/{% for %}, может содержать лишние пробелы и давать битые URL. Теги управления пробелами Jinja/Nunjucks ({%- ... -%}) убирают эти окружающие пробелы, и атрибут рендерится одним чистым значением. Атрибут class исключён: лишние пробелы между именами классов безвредны.

Не применяется к профилю django: теги шаблонов Django не поддерживают управление пробелами {%- -%}.

Неправильно:

<a href="{% if x %}/home{% endif %}"></a>

Правильно:

<a href="{%- if x -%}/home{%- endif -%}"></a>

H029

Рассмотрите возможность использования строчных значений метода формы.

Спецификация HTML определяет ключевые слова метода формы в нижнем регистре (get, post); варианты в верхнем регистре браузеры принимают лишь за счёт регистронезависимого сопоставления. Каноническая запись в нижнем регистре делает шаблоны единообразными и удобными для grep и избавляет от претензий строгих валидаторов и XHTML-инструментов.

Неправильно:

<form method="POST"></form>

Правильно:

<form method="post"></form>

H030

Рассмотрите возможность добавления мета-описания.

Поисковые системы используют meta description как сниппет под заголовком страницы в выдаче; без него они синтезируют сниппет из произвольного текста страницы, что снижает кликабельность и даёт плохие превью ссылок, когда страницей делятся.

Срабатывает только на файлах с полным документом <html></html>. Аргумент про сниппет относится к публично индексируемым страницам; для приложений за авторизацией или интранета это правило часто отключают.

Неправильно:

<html lang="en">
  <head><title>Home</title></head>
  <body>Welcome</body>
</html>

Правильно:

<html lang="en">
  <head>
    <title>Home</title>
    <meta name="description" content="A short summary of this page.">
  </head>
  <body>Welcome</body>
</html>

T032

В тегах шаблона обнаружены лишние пробелы.

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

Неправильно:

{% static  'css/style.css' %}

Правильно:

{% static 'css/style.css' %}

H033

В действии формы обнаружен лишний пробел.

Начальные и конечные пробелы внутри значения action формы становятся частью итогового URL. Браузеры убирают их при разборе, но небраузерные клиенты и тесты, обращающиеся к литеральному значению, могут этого не делать, а лишний пробел вокруг тега {% url %} почти всегда указывает на опечатку, из-за которой URL отправки формы не проходит сопоставление маршрутов на сервере.

Неправильно:

<form action="{% url 'search' %} " method="get">
    <button>Search</button>
</form>

Правильно:

<form action="{% url 'search' %}" method="get">
    <button>Search</button>
</form>

T034

Вы намеревались использовать {% ... %} вместо {% ... }%?

}% почти всегда опечатка вместо %}. Шаблонизатор не распознаёт }% как разделитель тега, поэтому тег вообще не разбирается: сырой текст {% … }% утекает в отрендеренный HTML, либо движок выбрасывает синтаксическую ошибку, наткнувшись на незакрытый тег.

Неправильно:

{% include "footer.html" }%

Правильно:

{% include "footer.html" %}

H036

Не используйте теги br для отступов.

Спецификация html разрешает <br> только для разрыва строки, который сам по себе является частью содержимого, как в почтовом адресе или стихотворении, и такое употребление не трогается. Помечается презентационное употребление, которое спецификация исключает: подряд идущие два и более разрыва, то есть вертикальный отступ, и разрыв, прижатый к внутреннему краю блочного элемента, который не рендерит ничего сверх его собственного margin. И то и другое ломает перенос текста на узких экранах, а скринридер объявляет принудительный разрыв там, где объявлять нечего.

Неправильно:

<p>Shipping is free.<br><br>Delivery takes 3 days.</p>

Правильно:

<p>Shipping is free.</p>
<p>Delivery takes 3 days.</p>

H037

Найдено дублирование атрибута.

Дублирующиеся атрибуты являются невалидным HTML: браузеры оставляют только первое вхождение и молча отбрасывают остальные, поэтому второе значение class или style никогда не применяется, что скрывает реальные ошибки. Проверка учитывает шаблоны: атрибут, повторённый во взаимоисключающих ветках ({% if %}/{% else %}), не помечается, поскольку отрендерится только одна копия.

Неправильно:

<div class="card" id="profile" class="active">...</div>

Правильно:

<div class="card active" id="profile">...</div>

T038

Блочный тег не имеет соответствующего закрывающего тега.

Блочный тег вроде {% if %}, {% for %} или {% macro %} без парного закрывающего тега означает гарантированный TemplateSyntaxError в Django и Jinja: страница не рендерится в момент запроса, а это правило ловит проблему до деплоя. Оно также помечает осиротевшие закрывающие теги без открывающего и неправильно переплетённые блоки (например, {% if %}``{% for %}``{% endif %}).

Парность {% block %}/{% endblock %} и несовпадающие имена endblock проверяются этим правилом; T003 (отключено по умолчанию) дополнительно требует имя у каждого многострочного {% endblock %}. Пользовательские блочные теги, зарегистрированные через custom_blocks, тоже проверяются, включая их самозакрывающуюся форму / %}.

Неправильно:

{% if user.is_authenticated %}
<p>Welcome back!</p>

Правильно:

{% if user.is_authenticated %}
<p>Welcome back!</p>
{% endif %}

T039

Найден незакрытый тег шаблона.

Тег шаблона, открытый {{ или {%, но не закрытый парным }} или %}, не разбирается как тег: Django/Jinja либо выбрасывают TemplateSyntaxError, либо выводят сырые фигурные скобки на страницу, а всё до следующего разделителя может быть молча поглощено. Такие опечатки (одна пропущенная скобка, несовпадающий разделитель) легко пропустить на ревью, потому что шаблон может частично рендериться.

Неправильно:

<p>{{ user.name }</p>

Правильно:

<p>{{ user.name }}</p>

T040

Отсутствующее или пустое имя шаблона в теге extends или include.

Тег {% extends %} или {% include %} с отсутствующим, пустым или состоящим из одних пробелов именем шаблона не может ничего загрузить: Django выбрасывает TemplateSyntaxError, когда имя отсутствует совсем, и TemplateDoesNotExist при рендеринге, когда оно пустое; страница отвечает 500 в продакшене, хотя сам файл шаблона выглядит синтаксически правдоподобно.

Неправильно:

{% extends "" %}

Правильно:

{% extends "base.html" %}

H041

Тег закрывается в другом блоке шаблона, чем тот, в котором он был открыт.

Когда HTML-тег открыт в одном {% block %}, а закрыт в другом, дочерний шаблон, переопределяющий только один из этих блоков, наследует половину элемента, и в отрендеренной странице появляется несбалансированная разметка: браузеры непредсказуемо автозакрывают или перевкладывают элементы, ломая вёрстку и CSS-селекторы далеко от фактически отредактированного шаблона. Если каждый элемент открывается и закрывается в одном и том же блоке, любой блок можно безопасно переопределять независимо.

Неправильно:

{% block content %}
<div class="wrapper">
{% endblock content %}
{% block footer %}
</div>
{% endblock footer %}

Правильно:

{% block content %}
<div class="wrapper">
</div>
{% endblock content %}
{% block footer %}
{% endblock footer %}

T041

Тег extends должен быть первым тегом в шаблоне.

Django отказывается компилировать шаблон, в котором перед {% extends %} стоит другой тег, а текст, записанный до него, рендерится, поэтому он утекает на страницу впереди всего, что выводит родительский шаблон. Jinja тоже рендерит этот текст, а nunjucks его отбрасывает, так что ни в одном движке шаблон не делает того, что, судя по виду, должен.

Комментарий {# #} ничего не рендерит и не считается, как и всё внутри блока, который djLint не проверяет, например блока {% comment %}, {% raw %} или {% verbatim %} или области {# djlint:off #}. Проверяется только первый {% extends %}; второй является отдельной ошибкой. Тег ветвления перед ним тоже не считается: jinja документирует {% if x %}{% extends "a.html" %}{% else %}{% extends "b.html" %}{% endif %} как способ выбрать родительский шаблон.

Не применяется к профилям handlebars, golang, liquid и angular.

Неправильно:

{% load static %}
{% extends "base.html" %}

Правильно:

{% extends "base.html" %}
{% load static %}

H042

Атрибут for у label не имеет соответствующего id в этом файле.

Проверка выполняется только для файлов, которые можно анализировать надёжно: если файл содержит что-либо, способное отрендерить id, которого в этом файле не видно (вывод {{ ... }} вроде виджета формы, {% include %} или {% extends %}, либо неизвестный тег шаблона), правило молчит для этого файла. Там, где оно работает, каждое срабатывание означает действительно сломанную связь.

Неправильно:

<label for="email">Email</label>
<input id="username">

Правильно:

<label for="email">Email</label>
<input id="email">

T042

Содержимое вне блока не рендерится в шаблоне, который расширяет другой.

Как только шаблон расширяет другой, родитель решает, что выводится, а потомок лишь заполняет блоки родителя. Текст или html, записанный после {% extends %} и вне всех {% block %}, молча отбрасывается при рендеринге, поэтому абзац, который в исходнике выглядит нормально, никогда не попадает на страницу.

Тег шаблона там по-прежнему выполняется, поэтому {% load %}, {% set %} и {% if %}, обёрнутый вокруг блока, не трогаются, как и комментарии {# #} и {% comment %}, блоки {% raw %} и {% verbatim %}, а также тело {% macro %} или блочной формы {% set %}, которое захватывается, а не выводится. Html-комментарий выводится как любой другой текст, поэтому о нём вне блока сообщается, как и о тексте {% blocktrans %}, который не является {% block %}. Учитывается только содержимое после тега extends, и о каждом его непрерывном участке сообщается один раз, в его начале.

Не применяется к профилям handlebars, golang, liquid и angular.

Неправильно:

{% extends "base.html" %}
<p>This paragraph is never shown.</p>
{% block content %}
<h1>Welcome</h1>
{% endblock %}

Правильно:

{% extends "base.html" %}
{% block content %}
<h1>Welcome</h1>
<p>This paragraph is shown.</p>
{% endblock %}

H043

Тег button должен иметь атрибут type.

У <button> без type он равен submit, поэтому кнопка, написанная ради вызова скрипта, заодно отправляет окружающую форму, и страница перезагружается. Явно указанный тип избавляет от этого.

Неправильно:

<form>
  <button onclick="preview()">Предпросмотр</button>
</form>

Правильно:

<form>
  <button type="button" onclick="preview()">Предпросмотр</button>
</form>

T043

Имя блока используется в шаблоне более одного раза.

Django, Jinja и Nunjucks отказываются разбирать шаблон, в котором два блока названы одинаково, поэтому страница вообще не загружается. Движкам всё равно, что два блока находятся в разных ветках {% if %}, поэтому имя каждого блока должно быть уникальным во всём файле, стоят ли блоки рядом или один вложен в другой.

Считается только {% block %}: {% blocktrans %} не является блоком, {% endblock name %} лишь называет блок, который закрывает, а блок, записанный внутри комментария, никогда не доходит до парсера. Имена сравниваются как записаны, поскольку движки считают Content и content двумя блоками.

Не применяется к профилям handlebars, golang, liquid и angular.

Неправильно:

{% block content %}{% endblock %}
{% block content %}{% endblock %}

Правильно:

{% block content %}{% endblock %}
{% block sidebar %}{% endblock %}

H044

В thead не следует смешивать ячейки th и td.

Каждая строка оценивается отдельно, поэтому пояснительная строка из td, которую спецификация html помещает в thead рядом со строкой заголовков, смешением не считается. Пустой td в начале строки это угловая ячейка таблицы с заголовками в первом столбце, разметка из руководства W3C по доступности, и она пропускается.

th и td значат разное для программы чтения с экрана и обычно оформлены разным css, поэтому одна случайная ячейка в строке заголовка читается как данные и выглядит иначе, чем соседние столбцы. Такая смесь допустима в html, из-за чего её и трудно заметить.

Неправильно:

<thead>
  <tr>
    <th>Имя</th>
    <td>Размер</td>
  </tr>
</thead>

Правильно:

<thead>
  <tr>
    <th>Имя</th>
    <th>Размер</th>
  </tr>
</thead>

T044

Тег вывода содержит ключевое слово инструкции; используйте блочный тег.

Не применяется к профилям golang, handlebars и angular.

Тег вывода печатает значение, а if, for, url, include и остальные являются инструкциями, место которым в блочном теге. Django, Jinja и Nunjucks отвергают {{ if x }} с синтаксической ошибкой, а закрывающее ключевое слово само по себе, например {{ endif }}, читается как переменная, которая ничего не рендерит, пока блок, который оно должно было закрыть, остаётся открытым, поэтому страница либо не загружается, либо показывает то, что условие должно было скрыть.

Голое ключевое слово является обычным именем переменной, поэтому о {{ url }}, {{ url|default:"/" }} и {{ set.name }} не сообщается. Сообщается только о ключевом слове, за которым следует аргумент, а также о закрывающем ключевом слове или ключевом слове ветки самом по себе, например {{ endif }} или {{ else }}. Выражение jinja, которое случайно начинается с одного из этих имён, например {{ url ~ "/x" }} или {{ url if url else "#" }}, не трогается.

Неправильно:

{{ if user.is_active }}

Правильно:

{% if user.is_active %}

H045

Тег iframe должен иметь атрибут title.

Программа чтения с экрана называет iframe по его доступному имени. Без имени она читает url кадра или молчит, и до перехода в кадр невозможно понять, что за страница в него встроена. WCAG относит это к 4.1.2 «Имя, роль, значение», а axe и html-validate включают такую проверку по умолчанию.

Имя может задавать title, aria-label или aria-labelledby, достаточно любого из трёх. Имя, записанное тегом шаблона, тоже считается, поэтому кадр, который называется по-своему на каждой странице, не отмечается.

Неправильно:

<iframe src="/report/"></iframe>

Правильно:

<iframe src="/report/" title="Квартальный отчёт"></iframe>

T045

Тег шаблона внутри html-комментария всё равно выполняется; чтобы его отключить, используйте комментарий шаблона.

Html-комментарий скрывает разметку от браузера, а не от шаблонизатора. <!-- {% include "debug.html" %} --> по-прежнему рендерит файл, а <!-- {% if debug %}...{% endif %} --> по-прежнему вычисляется, одинаково в Django, Jinja, Nunjucks, Handlebars и Go, поэтому тег, закомментированный таким способом, продолжает выполняться, а всё, что он выводит, попадает внутрь комментария или, если содержит -->, вырывается из него. Только комментарий шаблона, {# #} в Django и Jinja, {{! }} в Handlebars или {{/* */}} в Go, останавливает выполнение тега.

Сообщается только о теге-инструкции: {% %}, секции, закрытии или partial в handlebars и ключевом слове Go, например {{if}} или {{end}}. Значение, напечатанное в комментарий, как в <!-- built {{ version }} -->, является намеренным применением и не трогается, как и тег внутри комментария шаблона или блока {% comment %}, а также условный комментарий для Internet Explorer, <!--[if IE]> ... <![endif]-->, тело которого является разметкой для названного в нём браузера.

Неправильно:

<!-- {% include "banner.html" %} -->

Правильно:

{# {% include "banner.html" %} #}

H046

Значение tabindex не должно быть положительным.

Положительный tabindex ставит элемент в начало порядка обхода, впереди всего, у чего его нет. Одного такого значения достаточно, чтобы перестроить всю страницу для того, кто ходит по ней с клавиатуры, а дальше порядок приходится держать вручную в каждом шаблоне, который добавляет элемент управления. WCAG разбирает это в 2.4.3 «Порядок фокуса».

0 ставит элемент в порядок обхода там, где его помещает документ, а -1 убирает из обхода, оставляя фокусируемым из скрипта. Ни то, ни другое не отмечается, как и значение, записанное тегом шаблона: его число здесь неизвестно.

Неправильно:

<input tabindex="1">

Правильно:

<input tabindex="0">

H047

aria-hidden не следует ставить на фокусируемый элемент.

aria-hidden="true" убирает элемент из дерева доступности, но оставляет в порядке обхода, поэтому на нём по-прежнему останавливается клавиатура, а озвучивать нечего. Скрыть декоративную иконку это обычное применение атрибута, и оно не отмечается; отмечается только элемент, который получает фокус сам по себе.

Элемент считается фокусируемым, если это button, select, textarea, iframe или summary, a или area с href, не скрытый input, audio или video с controls, а также всё, что несёт contenteditable или tabindex от 0 и выше. Элемент с disabled или с tabindex="-1" уже вне порядка обхода и остаётся нетронутым.

Неправильно:

<button aria-hidden="true">Close</button>

Правильно:

<button type="button" aria-label="Close"><span aria-hidden="true">x</span></button>

H048

Такого aria-атрибута нет в спецификации.

Aria-атрибут с опечаткой не делает ничего. Браузер не предупреждает, программа чтения с экрана о нём не сообщает, а разметка сохраняет вид доступной, поэтому aria-lable может годами лежать в шаблоне, пока элемент, который он должен был называть, остаётся безымянным.

Правило знает имена, определённые в ARIA. Привязка, записанная фреймворком, не является простым aria-именем, поэтому :aria-label, v-bind:aria-label и [attr.aria-label] не проверяются.

Неправильно:

<button type="button" aria-lable="Close">x</button>

Правильно:

<button type="button" aria-label="Close">x</button>

H049

Viewport не должен запрещать масштабирование страницы.

user-scalable=no, а также maximum-scale меньше 2, не дают увеличить страницу на телефоне, а для многих это единственный способ её прочитать. WCAG требует 200 % в 1.4.4 «Изменение размера текста». Браузеры всё чаще игнорируют это ограничение, но там, где тег соблюдается, он по-прежнему выключает масштабирование.

Имя и содержимое находятся независимо от того, в каком порядке записаны эти два атрибута.

Неправильно:

<meta name="viewport" content="width=device-width, user-scalable=no">

Правильно:

<meta name="viewport" content="width=device-width, initial-scale=1">

H050

Элемент устарел, его следует заменить.

<center>, <font>, <big>, <strike> и <tt> исчезли, когда оформление перешло к css, а <marquee>, <blink>, <nobr> и <spacer> вообще никогда не были стандартными. Html больше не описывает ни один из них, поэтому ничто не гарантирует, как браузер их разместит, а таблица стилей не может обратиться к ним так, как обращается к классу.

Правило знает элементы acronym, applet, basefont, bgsound, big, blink, center, dir, font, frame, frameset, isindex, keygen, marquee, menuitem, nobr, noembed, noframes, plaintext, spacer, strike, tt и xmp. Сообщается только об открывающем теге, так что каждый элемент называется один раз, а пользовательский элемент, имя которого лишь начинается с одного из них, например <font-picker>, не трогается.

Неправильно:

<center><font color="red">Warning</font></center>

Правильно:

<p class="warning">Warning</p>

H051

Роль не входит в те, что ARIA определяет для разметки.

Роль, которую не называет ни одна спецификация, просто отбрасывается, и элемент сохраняет то значение, которое у него уже было: role="buton" оставляет <div> просто <div>, то есть ничем для программы чтения с экрана, хотя разметка выглядит так, будто ей задали назначение. Об этом ничто не предупреждает, поэтому такую опечатку и стоит ловить.

Правило знает роли, которые ARIA определяет для авторов, а также те, что добавляют DPUB-ARIA (doc-chapter и остальные) и GRAPHICS-ARIA (graphics-symbol и остальные). Абстрактные роли ARIA, такие как landmark и sectionhead, сообщаются: спецификация говорит, что они нужны для построения её онтологии и не должны записываться в разметке.

В role может стоять несколько имён, как список запасных вариантов, и проверяется каждое. Значение с синтаксисом шаблона узнать невозможно, и оно не трогается, как и привязка, написанная фреймворком, вроде :role или [attr.role].

Неправильно:

<div role="buton">Save</div>

Правильно:

<button type="button">Save</button>

H052

Meta refresh не должен перезагружать или перенаправлять страницу по таймеру.

Обновление по таймеру уводит страницу из-под того, кто её читает. Тот, кто читает медленно, пользуется программой чтения с экрана или просто отвлёкся, теряет своё место без предупреждения и без возможности это остановить, что нарушает WCAG 2.2.1 «Настройка времени», а обновление той же страницы выбрасывает всё, что в неё ввели.

Нулевая задержка — это немедленное перенаправление, а не таймер; WCAG допускает его там, где нельзя обойтись перенаправлением на сервере, поэтому о нём не сообщается. Оба атрибута находятся независимо от того, в каком порядке они записаны.

Неправильно:

<meta http-equiv="refresh" content="30">

Правильно:

<meta http-equiv="refresh" content="0; url=/next-page">

H053

Id используется в файле более одного раза.

Id называет один элемент. Второй элемент с тем же id ломает getElementById, <label for>, ссылки на фрагменты и aria-labelledby: браузер берёт первый и молча игнорирует остальные, поэтому подпись, ссылка или скрипт попадают не на тот элемент без всякого предупреждения.

Два id во взаимоисключающих ветках одного {% if %}...{% else %}...{% endif %} никогда не рендерятся вместе, поэтому о них не сообщается. Цикл {% for %} или {% block %} не является веткой: id внутри него и тот же id снаружи рендерятся оба, и о более позднем сообщается. Значение, записанное тегом шаблона, узнать невозможно, и оно не трогается, как и пустое значение. Id сравниваются точно, как это делает браузер, поэтому save и Save являются двумя id.

Неправильно:

<button type="submit" id="submit">Save</button>
<button type="submit" id="submit">Save and continue</button>

Правильно:

<button type="submit" id="submit">Save</button>
<button type="button" id="cancel">Cancel</button>

H054

Интерактивный элемент не следует вкладывать в другой.

Html запрещает интерактивное содержимое внутри <a> и <button>. Кнопка внутри ссылки или ссылка внутри кнопки является невалидной разметкой, которую браузеры чинят каждый по-своему, а пользователь программы чтения с экрана или клавиатуры получает один элемент управления, который ведёт себя как два. axe сообщает о том же как о «nested-interactive».

Отслеживаемые контейнеры это <a> с href и <button>, а элементы управления, о которых сообщается внутри них, это ссылка с href, button, input, select и textarea. <a> без href не интерактивен и не трогается с обеих сторон, как и скрытый input или тот, чей тип записывает тег шаблона. Незакрытая ссылка или кнопка заканчивается вместе с окружающим её элементом, как это было бы в браузере, поэтому одна опечатка не приводит к сообщениям обо всём остальном файле.

Неправильно:

<a href="/cart"><button>Add</button></a>

Правильно:

<a href="/cart" class="button">Add</a>

H055

Атрибут lang должен быть языковым тегом, например en или pt-BR.

H005 требует lang у <html>, но значение вроде lang="english" или lang="en_US" ему удовлетворяет, не называя ни одного языка, известного браузеру. Программа чтения с экрана тогда откатывается к голосу по умолчанию, а перевод и расстановка переносов выбирают не те правила или никаких. Значение должно быть тегом BCP 47: две или три буквы, затем любое число субтегов от одной до восьми букв или цифр, каждый после дефиса, как в en, pt-BR или zh-Hant-TW.

Проверяется только тег <html>, в соответствии с H005, а пустое значение оставлено тому правилу. Значение, записанное тегом шаблона, как в lang="{{ LANGUAGE_CODE }}", узнать невозможно, и оно не трогается, а xml:lang или data-lang не читается как lang.

Неправильно:

<html lang="english">

Правильно:

<html lang="en">

H056

Src не должен быть пустым.

Спецификация html говорит, что пустой src невалиден, и предупреждает, что браузер разрешает его относительно url самого документа, поэтому <img src=""> запрашивает страницу снова как изображение, а <script src=""></script> запрашивает её как скрипт. Обычно это заглушка, которую должен был заполнить скрипт, и исправление в том, чтобы убрать атрибут или держать значение в data-атрибуте, пока не появится настоящее. src, записанный вообще без значения, как в <img src>, тоже пуст, и о нём сообщается.

Проверяются только img, script, iframe, embed, source, track, audio и video, поскольку именно эти элементы запрашивают то, что называет src. Значение, записанное тегом шаблона, не трогается, как и значение из одних пробелов, а srcset и data-src являются другими атрибутами, о которых правило не судит.

Неправильно:

<img src="" alt="Logo">

Правильно:

<img src="{% static 'logo.png' %}" alt="Logo">

H057

Video должен иметь дорожку субтитров.

Видео со звуком несёт свою речь только в аудио, поэтому глухой или слабослышащий зритель без субтитров не получает от него ничего, а WCAG требует их в 1.2.2 «Субтитры (предзаписанные)». <track> с kind, равным captions или subtitles, внутри <video> удовлетворяет правилу, как и <track> вообще без kind, поскольку subtitles является значением по умолчанию.

У видео с muted нет аудио, которое нужно субтитровать, и о нём не сообщается. Как и о видео, в открывающем теге или теле которого стоит тег шаблона, поскольку дорожки или атрибут muted могут быть записаны шаблоном там, где djLint их не видит.

Неправильно:

<video controls src="talk.mp4"></video>

Правильно:

<video controls src="talk.mp4">
  <track kind="captions" src="talk.vtt" srclang="en">
</video>

{% endraw %}

Добавление правил

Мы приветствуем запросы с новыми правилами!

Хорошее правило состоит из

  • Name
  • Code
  • Message - Сообщение для отображения при обнаружении ошибки.
  • Flags - Флаги регекса. По умолчанию используется re.DOTALL. например: re.I|re.M
  • Patterns - regex-выражения, которые найдут ошибку.
  • Exclude - Необязательный список профилей, из которых нужно исключить правило.

Пожалуйста, включите тест для проверки правила.

Пользовательские правила

Создайте файл .djlint_rules.yaml рядом с вашим pyproject.toml. Правила могут быть добавлены в этот файл, и djLint подхватит их.
Файл правил в другом месте можно указать с помощью CLI-опции --rules.

Хорошее правило выглядит следующим образом:

- rule:
    name: T001
    message: Найти трихотилломанию
    flags: re.DOTALL|re.I
    patterns:
      - трихотилломанию