diff --git a/.github/workflows/a11y-check.yml b/.github/workflows/a11y-check.yml index 8eb3e9bc..15df7c13 100644 --- a/.github/workflows/a11y-check.yml +++ b/.github/workflows/a11y-check.yml @@ -33,15 +33,20 @@ jobs: run: pip install --require-hashes --no-deps -r requirements.txt - name: Build site - run: pelican content -o output -s publishconf.py + # SITEURL points at the local server: publishconf's absolute + # https://rivassec.com URLs made pa11y load the LIVE stylesheet, so a + # PR's CSS change was never what got tested (found 2026-10-23). + run: pelican content -o output -s publishconf.py -e 'SITEURL="http://127.0.0.1:8000"' - name: Set up Node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: - node-version: "20" + node-version: "22" - name: Install pa11y-ci - run: npm install --global pa11y-ci@3.1.0 + # pa11y-ci 5 (pa11y 10, axe 4.13) for levelCapWhenNeedsReview: pa11y 6 + # reported axe "incomplete" (needs review) results as errors. + run: npm install --global pa11y-ci@5.0.0 - name: Serve built site run: | @@ -57,13 +62,14 @@ jobs: done - name: Run pa11y-ci - # Non-blocking for now (2026-06-12). Pa11y is reporting contrast - # ratios that disagree with computed styles I can reproduce - # locally. Until I can attach a real Chrome devtools session to - # the live page and verify the actual computed colors on .c1 - # spans and the hero h1, this workflow logs findings without - # blocking pushes. Flip to blocking once the residual findings - # are reproduced and either fixed or proven to be axe gradient - # false-positives. + # Still non-blocking. The 2026-06-12 contrast "errors" were axe + # needs-review results (the hero gradient overlay hides the + # background; measured worst case is 5.5:1, above AA), which pa11y 6 + # reported as errors. levelCapWhenNeedsReview now keeps them as + # warnings. Every page in the sitemap is scanned. Flip to blocking + # in its own PR once the run is clean. continue-on-error: true - run: pa11y-ci --config .pa11yci.json + run: >- + pa11y-ci --config .pa11yci.json + --sitemap http://127.0.0.1:8000/sitemap.xml + --sitemap-exclude '\.(txt|md|xml)$' diff --git a/.pa11yci.json b/.pa11yci.json index fb852f78..a1bd578b 100644 --- a/.pa11yci.json +++ b/.pa11yci.json @@ -3,18 +3,17 @@ "standard": "WCAG2AA", "timeout": 30000, "wait": 500, - "runners": ["axe", "htmlcs"], + "runners": [ + "axe", + "htmlcs" + ], "chromeLaunchConfig": { - "args": ["--no-sandbox", "--disable-dev-shm-usage"] - } + "args": [ + "--no-sandbox", + "--disable-dev-shm-usage" + ] + }, + "levelCapWhenNeedsReview": "warning" }, - - "urls": [ - "http://127.0.0.1:8000/", - "http://127.0.0.1:8000/prompt-injection-supply-chain-evasion.html", - "http://127.0.0.1:8000/iam-safe-defaults-fail-loud.html", - "http://127.0.0.1:8000/leap-second-chaos-2012.html", - "http://127.0.0.1:8000/teensy-efi-bruteforce-hours-late.html", - "http://127.0.0.1:8000/venezuela-twitter-proxy-osint.html" - ] + "urls": [] } diff --git a/content/static/copy-code.js b/content/static/copy-code.js index 8764275b..5119874d 100644 --- a/content/static/copy-code.js +++ b/content/static/copy-code.js @@ -45,6 +45,9 @@ for (var i = 0; i < blocks.length; i++) { var pre = blocks[i]; if (pre.querySelector('.copy-btn')) continue; + // A
inside role="img" is a drawn diagram, not code: a button
+ // there is a nested interactive control (axe nested-interactive).
+ if (pre.closest('[role="img"]')) continue;
pre.classList.add('has-copy');
var btn = document.createElement('button');
btn.className = 'copy-btn';
diff --git a/content/static/custom.css b/content/static/custom.css
index 3894c571..451d2478 100644
--- a/content/static/custom.css
+++ b/content/static/custom.css
@@ -943,3 +943,19 @@ main footer a {
.series-nav li { margin: 2px 0; line-height: 1.5; }
.series-nav a { color: var(--accent); }
.series-nav [aria-current="page"] { font-weight: 600; color: var(--fg); }
+
+/* Links inside running text are underlined (WCAG 1.4.1, 2026-10-23): the
+ accent blue is only 1.78:1 against body text, so color alone does not
+ mark them. Navigation, cards and pills keep their own styles. */
+main p a,
+main li a,
+main td a,
+footer p a {
+ text-decoration: underline;
+ text-underline-offset: 0.15em;
+ text-decoration-thickness: 1px;
+}
+main p a:hover,
+main li a:hover,
+main td a:hover,
+footer p a:hover { text-decoration-thickness: 2px; }