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
14 changes: 7 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22'

- name: Build site
run: node scripts/build.js
Expand All @@ -86,36 +86,36 @@ jobs:
fi

- name: Run skill-a11y-audit (WCAG 2.1 AA)
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0
with:
serve-path: ./docs
sitemap: http://localhost:8088/sitemap.xml
sitemap-find: ${{ steps.site.outputs.base }}
sitemap-replace: http://localhost:8088
fail-on: errors
fail-on: major

- name: Audit canonical root landing
if: github.repository == 'snapsynapse/knowledge-as-code-template'
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0
with:
serve-path: ./
port: 8089
sitemap: http://localhost:8089/tests/fixtures/a11y-root-sitemap.xml
sitemap-find: https://knowledge-as-code.com
sitemap-replace: http://localhost:8089
fail-on: errors
fail-on: major
artifact-name: a11y-root-results

- name: Audit canonical demo deployment
if: github.repository == 'snapsynapse/knowledge-as-code-template'
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main
uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0
with:
serve-path: ./demo
port: 8090
sitemap: http://localhost:8090/sitemap.xml
sitemap-find: https://knowledge-as-code.com/demo
sitemap-replace: http://localhost:8090
fail-on: errors
fail-on: major
artifact-name: a11y-demo-results

# GitHub Pages serves directly from `main` branch root on this canonical repo.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ pattern.

## [Unreleased]

### Changed
- Pinned all three accessibility scan surfaces to A11y Audit v3.1.0 at its verified immutable commit, selected Node.js 22 explicitly for the scanner, and adopted the fail-closed major-findings policy.

### Fixed
- Repaired shared generated-site and canonical landing contrast, link distinction, decorative marker, matrix-label, and theme-control behavior found while scanning all 45 required routes.

## [1.2.0] - 2026-09-05

### Added
Expand Down
4 changes: 4 additions & 0 deletions MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ The maintained public path is:

The current supported runtime is Node.js 18 or later on macOS and Linux. Windows may work but is not part of the tested support promise.

## Accessibility gate

The canonical CI scans the generated template, canonical root landing, and canonical demo with the immutable A11y Audit identity and fail-closed major-findings policy recorded in [the 2026-09-07 accessibility adoption evidence](audits/a11y-2026-09-07.md). The generator keeps its Node.js 18-or-later support promise; the accessibility job selects its scanner's required Node.js 22 runtime explicitly.

## Maintenance priorities

Work is prioritized in this order:
Expand Down
8 changes: 4 additions & 4 deletions MANIFEST.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ files:

- path: scripts/build.js
role: tooling
hash: sha256:0b9445770253923bee61b49b90a883bf3db3a6511777b9da928ae26022f8382a
hash: sha256:ad7861d1350c55c69294d08bbaa175ffe9b11b53139f17b8ac7ebed58bea3191
note: Static site generator

- path: scripts/assets/favicon.svg
Expand Down Expand Up @@ -122,7 +122,7 @@ files:

- path: scripts/eval.js
role: tooling
hash: sha256:7f5622d2245588bf7cdf00cd2afcf651f7b9a44565b3572645b931bdfcfd0e49
hash: sha256:f87c45678844465c5d84bb838664802cfa3aeaab397f3b60cddda33a6ad4a043
note: Behavioral and security regression suite

- path: mcp-server.js
Expand Down Expand Up @@ -157,7 +157,7 @@ files:

- path: index.html
role: protocol
hash: sha256:8a431b58a12102a94602548fa3810ce348e10b5e2701e0a708649c1c78c595f7
hash: sha256:2c13538b1d72fb4b365f54e77a41a9697fc77f71d9ab26622e55e47147f9f8db
note: Canonical landing and executable quick start

- path: sitemap.xml
Expand All @@ -182,7 +182,7 @@ files:

- path: MAINTENANCE.md
role: protocol
hash: sha256:0d1fde34cb47dc6a25e71ff9bbfa28adb23ebf41bc99167850d919369881457d
hash: sha256:51a5afe6c658692a7d1789dc695d64451ba7c4660719e43a6776b1ef03c8e4fa
note: Compatibility and upgrade policy

- path: VERIFICATION.md
Expand Down
47 changes: 47 additions & 0 deletions audits/a11y-2026-09-07.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Accessibility adoption evidence

Date: 2026-09-07

## Action and policy identity

The three required accessibility scans use A11y Audit v3.1.0 at immutable commit `6af2c95a56ff058cddf0c6febc71512db1d6d19b`. The public annotated tag `refs/tags/v3.1.0` resolves through tag object `1fccece25b28c99bcbbf26881edc336c586f6d5e` to that commit. This relationship was independently verified from the public Git remote before adoption.

The accessibility job selects Node.js 22 explicitly. The action requires Node.js 22.12.0 or later and runs axe-core 4.12.1 through Puppeteer 25.10.0. Local acceptance used Node.js 26.7.0, which satisfies that runtime floor, with the same scanner dependencies from the v3.1.0 source checkout.

All three scans use `fail-on: major` without a baseline. Confirmed critical or serious standards violations block. Moderate, minor, and best-practice-only findings remain advisory. Unknown violation impact, potentially major incomplete evidence, missing coverage, and scan errors cannot pass.

## Built surfaces and route coverage

The tested source was the clean v1.2.0 baseline at `51abb92806393ae4ff0e6e671e77fe383ae0f690` plus the reviewed accessibility changes described below.

| Surface | Build or source identity | Required routes | Result | Result SHA-256 |
|---|---|---:|---|---|
| Template default | `node scripts/build.js`, transient `docs/`, `https://example.com/` rewritten to the local origin | 22/22 sitemap routes | Pass | `67e34ff3d895103d4edbbfebe74f795cd1fb711779e075e8d3593ce914d8cfec` |
| Canonical root landing | Hand-maintained root `index.html`, root fixture sitemap, Pages root unchanged | 1/1 reviewed route | Pass | `e1492560ce7ae26b34d57ef3f3558871b2013d6c65b1eac4e186d0ae55127f30` |
| Canonical demo | `KAC_OUTPUT_DIR=demo KAC_SITE_URL=https://knowledge-as-code.com/demo/ KAC_REPO_URL=https://github.com/snapsynapse/knowledge-as-code-template node scripts/build.js` | 22/22 sitemap routes | Pass | `d8cbce799fe5ec0b90def3a6b127d6b034b3c61fe2f479ad480a5f61dafef2fc` |

Each final result contains zero confirmed major findings, zero nonblocking findings, zero unknown-impact violations, zero blocking or advisory incomplete candidates, and zero scan errors. The result files are local execution evidence and are identified here by digest; they are not shipped as product artifacts.

The initializer golden path passed after the source changes. It continues to generate the supported fork structure and derives `.github/workflows/pages.yml` without moving the canonical repository's root Pages source. The generator's Node.js 18 and 20 compatibility matrix also remains in place; Node.js 22 is specific to the accessibility job.

## Findings and repairs

The initial template and demo scans had no confirmed violations, but each was inconclusive with 380 serious incomplete candidates across all 22 routes. A body gradient prevented deterministic contrast evaluation for 296 color candidates and 84 links. The root landing had one confirmed serious link distinction violation on the `PUBLISH-MCP.md` link plus 14 serious incomplete candidates involving a roleless labeled wordmark, decorative punctuation, and gradient-backed text.

Repairs were made in the owning sources and regenerated into both output trees:

- The generated site uses the existing dark or light background token as a solid page background so text contrast can be evaluated deterministically.
- Prose, list, breadcrumb, and footer links are visibly underlined.
- Timeline rails and markers are real decorative elements, relationship arrows are expressed as the words “maps to,” matrix counts have descriptive accessible names, and empty matrix cells retain “Not covered” text for assistive technology.
- The root wordmark has an image role, decorative separators and file arrows were removed, the confirmed link is underlined, and promotional statistics retain real text on a solid backing over the decorative gradient.
- The generated skip link and back-to-top control choose black or white foreground text from each configured accent color.

No axe rule was disabled, no route was skipped, and no baseline was created or refreshed.

## Theme checks

Targeted axe checks covered the root landing under light and dark `prefers-color-scheme` emulation. The generated template and canonical demo index, matrix, and timeline pages were checked after applying and settling the same `light-mode` class changed by their theme button, and again in the default dark state. All 14 page-and-theme cases returned zero violations and zero incomplete candidates.

The root highlighted heading text measured 17.40:1 against the light page background and 16.34:1 against the light highlight; the dark equivalents were 16.62:1 and 13.69:1. The generated back-to-top glyph measured 7.29:1 in light mode and 10.48:1 in dark mode. Its control boundary measured 6.70:1 and 8.51:1 respectively.

This automated result covers only the routes, themes, and rules described here. It does not establish complete WCAG conformance or replace manual testing.
4 changes: 3 additions & 1 deletion demo/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>
<script>
(function() {
Expand Down Expand Up @@ -109,7 +111,7 @@ <h1 style="margin-top:2rem;">404 — Page Not Found</h1>
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
6 changes: 4 additions & 2 deletions demo/about.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>
<meta name="description" content="How Example Knowledge Base is sourced, verified, and maintained: 2 frameworks, 3 requirements, and 4 provisions from 2 organizations.">
<meta property="og:title" content="About">
Expand Down Expand Up @@ -79,7 +81,7 @@ <h1><a href="index.html" onclick="passTheme(this)">Example Knowledge Base</a></h
<h2 style="margin-top: 0.5rem;">About</h2>
<p>A structured reference tracking example entities across categories. Tracks <strong>2 frameworks</strong>, <strong>3 requirements</strong>, <strong>4 provisions</strong>, and <strong>2 organizations</strong>.</p>
<h3>Data Model</h3>
<p><strong>Organization</strong> &rarr; <strong>Framework</strong> &rarr; <strong>Provision</strong> &rarr; <strong>Requirement</strong></p>
<p><strong>Organization</strong> maps to <strong>Framework</strong> maps to <strong>Provision</strong> maps to <strong>Requirement</strong></p>
<p>Requirements are the stable anchors. Provisions are the implementations — different frameworks implements the same requirements differently.</p>
<h3>JSON API</h3>
<ul>
Expand Down Expand Up @@ -129,7 +131,7 @@ <h3>Contributing</h3>
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
4 changes: 3 additions & 1 deletion demo/applies-to/federal/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>

<meta name="description" content="1 framework that applies to Federal, with status and effective dates.">
Expand Down Expand Up @@ -129,7 +131,7 @@ <h1><a href="../../index.html" onclick="passTheme(this)">Example Knowledge Base<
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
4 changes: 3 additions & 1 deletion demo/applies-to/international/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>

<meta name="description" content="1 framework that applies to International, with status and effective dates.">
Expand Down Expand Up @@ -129,7 +131,7 @@ <h1><a href="../../index.html" onclick="passTheme(this)">Example Knowledge Base<
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
34 changes: 26 additions & 8 deletions demo/assets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ html {
top: 0;
}

.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

/* ========================================
CSS VARIABLES (DARK MODE DEFAULT)
======================================== */
Expand Down Expand Up @@ -125,7 +137,7 @@ html {

body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, var(--bg-gradient-start) 0%, var(--bg-gradient-end) 100%);
background: var(--bg-gradient-start);
min-height: 100vh;
color: var(--text-primary);
padding: 20px;
Expand All @@ -137,6 +149,14 @@ body {
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

#main-content p a,
#main-content li a,
.breadcrumb a,
footer p a {
text-decoration: underline;
text-underline-offset: 0.12em;
}

h2 {
font-size: 1.5rem;
color: var(--text-heading);
Expand Down Expand Up @@ -702,8 +722,7 @@ th[data-sortable].sort-desc::after {
position: relative;
}

.timeline::before {
content: '';
.timeline-track {
position: absolute;
left: 20px;
top: 0;
Expand All @@ -726,8 +745,7 @@ th[data-sortable].sort-desc::after {
margin: 4px 0;
}

.timeline-entry::before {
content: '';
.timeline-marker {
position: absolute;
left: 14px;
top: 18px;
Expand All @@ -738,17 +756,17 @@ th[data-sortable].sort-desc::after {
background: var(--bg-gradient-start);
}

.timeline-entry.past::before {
.timeline-entry.past .timeline-marker {
background: var(--status-enforcing);
border-color: var(--status-enforcing);
}

.timeline-entry.future::before {
.timeline-entry.future .timeline-marker {
background: transparent;
border-color: var(--accent);
}

.timeline-entry.today-marker::before {
.timeline-entry.today-marker .timeline-marker {
background: var(--accent);
border-color: var(--accent);
box-shadow: 0 0 8px var(--accent);
Expand Down
4 changes: 3 additions & 1 deletion demo/authority/iso/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>

<meta name="description" content="International Organization for Standardization — 1 framework published by this organization.">
Expand Down Expand Up @@ -133,7 +135,7 @@ <h3>Frameworks (1)</h3>
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
4 changes: 3 additions & 1 deletion demo/authority/nist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>

<meta name="description" content="National Institute of Standards and Technology — 1 framework published by this organization.">
Expand Down Expand Up @@ -133,7 +135,7 @@ <h3>Frameworks (1)</h3>
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
4 changes: 3 additions & 1 deletion demo/compare.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
:is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; }
:root { --accent: #4fc3f7; }
:is(html, body).light-mode { --accent: #0055aa; }
.skip-link, .back-to-top { color: #000; }
:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }
</style>
<meta name="description" content="Compare any two or three of 2 frameworks side by side to see where their coverage overlaps and diverges.">
<meta property="og:title" content="Compare">
Expand Down Expand Up @@ -158,7 +160,7 @@ <h3>Side-by-side pages</h3>
var btn = document.createElement('button');
btn.className = 'back-to-top';
btn.setAttribute('aria-label', 'Back to top');
btn.textContent = '\u2191';
btn.innerHTML = '<span aria-hidden="true">\u2191</span>';
document.body.appendChild(btn);
window.addEventListener('scroll', function() { btn.classList.toggle('visible', window.scrollY > 400); });
btn.addEventListener('click', function() { window.scrollTo({ top: 0, behavior: 'smooth' }); });
Expand Down
Loading
Loading