Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions .github/workflows/a11y-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand All @@ -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)$'
23 changes: 11 additions & 12 deletions .pa11yci.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": []
}
3 changes: 3 additions & 0 deletions content/static/copy-code.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
for (var i = 0; i < blocks.length; i++) {
var pre = blocks[i];
if (pre.querySelector('.copy-btn')) continue;
// A <pre> 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';
Expand Down
16 changes: 16 additions & 0 deletions content/static/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Loading