From 8f3197862d3d523c27c9e8f78949ff87cfd04e05 Mon Sep 17 00:00:00 2001 From: Grischan Date: Mon, 30 Mar 2026 11:00:40 +0200 Subject: [PATCH 1/2] [ST-1816] a11y:Fix Html Errors --- meinberlin/apps/captcha/assets/captcheck.js | 3 +- .../captcheck_captcha_widget.html | 2 +- .../includes/form_field.html | 8 + .../meinberlin_plans/plan_detail.html | 2 +- .../includes/module-tile/module_tile.html | 2 +- .../includes/status_bar.html | 13 +- .../project_information.html | 2 +- tests/html_audit/ASANA_SUBTASK_MAPPING.md | 56 ++++ tests/html_audit/__init__.py | 1 + .../test_nu_html_checker_audit_messages.py | 249 ++++++++++++++++++ 10 files changed, 324 insertions(+), 14 deletions(-) create mode 100644 tests/html_audit/ASANA_SUBTASK_MAPPING.md create mode 100644 tests/html_audit/__init__.py create mode 100644 tests/html_audit/test_nu_html_checker_audit_messages.py diff --git a/meinberlin/apps/captcha/assets/captcheck.js b/meinberlin/apps/captcha/assets/captcheck.js index 3adcafc9ae..1e8653a5ac 100644 --- a/meinberlin/apps/captcha/assets/captcheck.js +++ b/meinberlin/apps/captcha/assets/captcheck.js @@ -88,7 +88,8 @@ function initializeCaptcha () { /* Loop over all the CAPTCHA containers on the page, setting up a different CAPTCHA in each */ Array.prototype.forEach.call(document.getElementsByClassName('captcheck_container'), function (container) { const apiUrl = container.getAttribute('data-api_url') - const combinedAnswerId = container.getAttribute('combined_answer_id') + const combinedAnswerId = + container.dataset.combinedAnswerId || container.getAttribute('combined_answer_id') const xhr = new XMLHttpRequest() xhr.open('GET', apiUrl + '?action=new', true) xhr.onreadystatechange = function () { diff --git a/meinberlin/apps/captcha/templates/meinberlin_captcha/captcheck_captcha_widget.html b/meinberlin/apps/captcha/templates/meinberlin_captcha/captcheck_captcha_widget.html index 91aac9c913..88134b1b4c 100644 --- a/meinberlin/apps/captcha/templates/meinberlin_captcha/captcheck_captcha_widget.html +++ b/meinberlin/apps/captcha/templates/meinberlin_captcha/captcheck_captcha_widget.html @@ -1,2 +1,2 @@ -
+
diff --git a/meinberlin/apps/contrib/templates/meinberlin_contrib/includes/form_field.html b/meinberlin/apps/contrib/templates/meinberlin_contrib/includes/form_field.html index dab2805a96..bd40cd11c0 100644 --- a/meinberlin/apps/contrib/templates/meinberlin_contrib/includes/form_field.html +++ b/meinberlin/apps/contrib/templates/meinberlin_contrib/includes/form_field.html @@ -1,14 +1,22 @@ {% load i18n widget_tweaks %}
+ {% if field.field.widget.is_hidden %} +
+ {% else %}
+ {% else %} + {% endif %} {% if field.help_text and not field.name == "password" %} diff --git a/meinberlin/apps/plans/templates/meinberlin_plans/plan_detail.html b/meinberlin/apps/plans/templates/meinberlin_plans/plan_detail.html index f3ad48e901..6b257c9519 100644 --- a/meinberlin/apps/plans/templates/meinberlin_plans/plan_detail.html +++ b/meinberlin/apps/plans/templates/meinberlin_plans/plan_detail.html @@ -122,7 +122,7 @@ {% if object.has_contact_details or object.organisation %}
-

{% trans 'Contact Information' %}

+

{% trans 'Contact Information' %}

{% include 'meinberlin_projects/includes/contact_person.html' with contact=object only %} {% include 'meinberlin_projects/includes/contact_organisation.html' with organisation=object.organisation only %}
diff --git a/meinberlin/apps/projects/templates/meinberlin_projects/includes/module-tile/module_tile.html b/meinberlin/apps/projects/templates/meinberlin_projects/includes/module-tile/module_tile.html index dc253a97c0..ae8fc8d90d 100644 --- a/meinberlin/apps/projects/templates/meinberlin_projects/includes/module-tile/module_tile.html +++ b/meinberlin/apps/projects/templates/meinberlin_projects/includes/module-tile/module_tile.html @@ -10,7 +10,7 @@ {% if module.module_running_time_left %} - {% include "meinberlin_projects/includes/status_bar.html" with progress=module.module_running_progress uniqueId="module-running-progress-{{ module.pk }}" time_left=module.module_running_time_left %} + {% include "meinberlin_projects/includes/status_bar.html" with progress=module.module_running_progress progress_bar_id=module.pk time_left=module.module_running_time_left %} {% elif not module.module_has_started %} diff --git a/meinberlin/apps/projects/templates/meinberlin_projects/includes/status_bar.html b/meinberlin/apps/projects/templates/meinberlin_projects/includes/status_bar.html index 03de6ddb52..441e44c3f0 100644 --- a/meinberlin/apps/projects/templates/meinberlin_projects/includes/status_bar.html +++ b/meinberlin/apps/projects/templates/meinberlin_projects/includes/status_bar.html @@ -3,18 +3,13 @@ {{ progress }}% -{% if not uniqueId %} -

Please add a uniqueId to status_bar.html includes!

-{% endif %} - +
diff --git a/meinberlin/apps/projects/templates/meinberlin_projects/project_information.html b/meinberlin/apps/projects/templates/meinberlin_projects/project_information.html index 862abca1e0..71908fabfd 100644 --- a/meinberlin/apps/projects/templates/meinberlin_projects/project_information.html +++ b/meinberlin/apps/projects/templates/meinberlin_projects/project_information.html @@ -42,7 +42,7 @@

{% if project.has_contact_details or project.organisation %}
-

{% trans 'Contact Information' %}

+

{% trans 'Contact Information' %}

{% include 'meinberlin_projects/includes/contact_person.html' with contact=project only %} {% include 'meinberlin_projects/includes/contact_organisation.html' with organisation=project.organisation only %}
diff --git a/tests/html_audit/ASANA_SUBTASK_MAPPING.md b/tests/html_audit/ASANA_SUBTASK_MAPPING.md new file mode 100644 index 0000000000..86a4d1e7ba --- /dev/null +++ b/tests/html_audit/ASANA_SUBTASK_MAPPING.md @@ -0,0 +1,56 @@ +# Audit subtasks vs this PR (errors / non-heading warnings) + +Parent: [ST-1816 — Fix HTML Errors](https://app.asana.com/1/1175467295883992/task/1213806008749002) · workspace `1175467295883992` + +Only subtasks that had **at least one Nu Html Checker error** (or a non–empty-heading warning still tracked here) are listed. Subtasks that documented **only** the empty-heading warning are omitted. + +Legend: **Fixed** = addressed in this codebase. **Not fixed** = still open or out of scope. + +--- + +### [1213806008749008](https://app.asana.com/1/1175467295883992/task/1213806008749008) — `mein.berlin.de/` (2 errors, 4 heading warnings in audit table) + +- **Fixed (errors):** `aria-valuemax` (and related redundant ARIA) with native `max` on ``; `

', + re.IGNORECASE | re.DOTALL, + ) + offenders: list[str] = [] + for path in REPO_ROOT.glob(MEINBERLIN_HTML_GLOB): + if not path.is_file(): + continue + try: + data = path.read_text(encoding="utf-8") + except OSError: + continue + if pattern.search(data): + offenders.append(str(path.relative_to(REPO_ROOT))) + assert not offenders, f"{msg} Found in: {offenders}" + + +def test_article_icon_list_single_must_have_heading_or_not_use_article(): + """ + Nu Html Checker: Article lacks heading. Consider using h2-h6 inside article. + + Applies when a teaser uses
(or similar). + """ + path = ( + REPO_ROOT + / "meinberlin/apps/cms/templates/meinberlin_cms/blocks/icon_block.html" + ) + text = path.read_text(encoding="utf-8") + msg = ( + 'Nu Html Checker: Article lacks heading. Consider using "h2"-"h6" elements ' + "to add identifying headings to all articles. " + "(icon-list__single must not be a bare
without a heading.)" + ) + if "; keep guard for future regressions. + assert "icon-list__single" in text + return + assert re.search(r" list[str]: + violations: list[str] = [] + for m in re.finditer( + r"]*>(.*?)
", + fragment, + re.IGNORECASE | re.DOTALL, + ): + inner = m.group(1) + if "icon-list__single" in m.group(0) and not re.search( + r"' + '' + "

text

" + "
" + ) + msgs = check_articles_have_headings(bad) + assert msgs, "expected synthetic snippet to produce a heading warning message" + assert "Article lacks heading" in msgs[0] From dcc3ecfc14c750551f831fcb5ba5ce27cab10403 Mon Sep 17 00:00:00 2001 From: Grischan Date: Mon, 30 Mar 2026 11:08:12 +0200 Subject: [PATCH 2/2] Add Changelog --- CHANGELOG.md | 4 ++ tests/html_audit/ASANA_SUBTASK_MAPPING.md | 56 ----------------------- 2 files changed, 4 insertions(+), 56 deletions(-) delete mode 100644 tests/html_audit/ASANA_SUBTASK_MAPPING.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 56db78c591..a7038e481a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project (not yet) adheres to [Semantic Versioning](https://semver.org/spec/ ## Unreleased +### Fixed + +- HTML errors (subdomain audit) + ### Changed - Project and plan tiles: placeholder tile image copyright shows Senatskanzlei Berlin diff --git a/tests/html_audit/ASANA_SUBTASK_MAPPING.md b/tests/html_audit/ASANA_SUBTASK_MAPPING.md deleted file mode 100644 index 86a4d1e7ba..0000000000 --- a/tests/html_audit/ASANA_SUBTASK_MAPPING.md +++ /dev/null @@ -1,56 +0,0 @@ -# Audit subtasks vs this PR (errors / non-heading warnings) - -Parent: [ST-1816 — Fix HTML Errors](https://app.asana.com/1/1175467295883992/task/1213806008749002) · workspace `1175467295883992` - -Only subtasks that had **at least one Nu Html Checker error** (or a non–empty-heading warning still tracked here) are listed. Subtasks that documented **only** the empty-heading warning are omitted. - -Legend: **Fixed** = addressed in this codebase. **Not fixed** = still open or out of scope. - ---- - -### [1213806008749008](https://app.asana.com/1/1175467295883992/task/1213806008749008) — `mein.berlin.de/` (2 errors, 4 heading warnings in audit table) - -- **Fixed (errors):** `aria-valuemax` (and related redundant ARIA) with native `max` on ``; `