From 86c0d573766595d0c4a55abf2f2f1b0b61e69812 Mon Sep 17 00:00:00 2001 From: Clara Vanacker Date: Mon, 20 Jul 2026 12:59:17 +0200 Subject: [PATCH] fix(a11y): label form controls, add a main landmark, fix light-mode contrast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lighthouse accessibility was 88 on the landing page and 96 on the paste page. SEO is already 100 since the sitemap work, so nothing was needed there. Form controls had no accessible name. The visible text lives in
, while
+
{% block page %}{% endblock %} +
{% block scripts %}{% endblock %} diff --git a/templates/index.html b/templates/index.html index 6191740..14072f4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -28,8 +28,8 @@

- Language - {% for lang in languages %}{% if lang %} @@ -50,10 +50,10 @@

-
Expiration
+
Expiration
Content permanently deleted after this delay.
- {% for value, label in ttl_options.items() %} {% endfor %} @@ -62,60 +62,60 @@

-
Max views
+
Max views
Delete after N reads. Leave empty for unlimited.
- +
-
Burn after read
+
Burn after read
Deleted permanently after the first view.
-
Compress before encrypting
+
Compress before encrypting
Gzip the plaintext locally before AES-GCM. 60–80% smaller for text/JSON/code.
-
Password protection
+
Password protection
Content is encrypted with your password.
- + - +
- Key derivation - @@ -124,17 +124,17 @@

-
Webhook on read
+
Webhook on read
POST to this URL each time the paste is viewed.
- +

diff --git a/tests/test_api.py b/tests/test_api.py index 4433d46..0ae086e 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -502,3 +502,20 @@ async def test_security_headers_present(client): assert r.headers.get("x-frame-options") == "DENY" assert r.headers.get("referrer-policy") == "no-referrer" assert "default-src 'self'" in r.headers.get("content-security-policy", "") + + +def test_css_defines_every_custom_property_it_uses(): + """An undefined var() is invalid at computed-value time, so the declaration + is dropped and the element silently inherits its parent's value instead. + That is how `.owner-notice-sub` ended up rendering in Dracula cyan for + months: it asked for var(--muted), which never existed.""" + import re + from pathlib import Path + + css = (Path(__file__).resolve().parent.parent / "static" / "style.css").read_text() + defined = set(re.findall(r"(--[\w-]+)\s*:", css)) + # var(--x, fallback) is legitimate even when --x is undefined; only bare + # references are a bug. + used = set(re.findall(r"var\(\s*(--[\w-]+)\s*\)", css)) + missing = sorted(used - defined) + assert not missing, f"style.css uses undefined custom properties: {missing}"