Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4bb891d
test(a11y): add pinned local axe-core WCAG A/AA audit with per-state …
sanskarIN Nov 6, 2026
6651b6e
test(browser): audit 10 bilingual offline UI states for WCAG A/AA and…
sanskarIN Nov 6, 2026
8066d37
build(a11y): pin offline axe-core WCAG auditing dependency
sanskarIN Nov 6, 2026
344ee28
ci(a11y): attach English/Hindi WCAG audit JSON evidence on browser te…
sanskarIN Nov 6, 2026
b791a67
chore(a11y): ignore local axe browser report files
sanskarIN Nov 6, 2026
78b4caa
docs(maintenance): explain audited states, scope and manual screen re…
sanskarIN Nov 6, 2026
8a75c02
docs(readme): surface offline accessibility audit gate and scope
sanskarIN Nov 6, 2026
6d6aa0a
docs(offline): explain new localized WCAG audit and narrow viewport s…
sanskarIN Nov 6, 2026
888d23c
docs(hi-offline): document WCAG rules and human-review limits in Hindi
sanskarIN Nov 6, 2026
bb5a11a
fix(a11y): include visible search keyboard shortcut in English/Hindi …
sanskarIN Nov 6, 2026
7fd355b
test(a11y): enforce label-in-name compliance for both search translat…
sanskarIN Nov 6, 2026
42a841b
test(a11y): add negative English/Hindi regression cases for visible s…
sanskarIN Nov 6, 2026
323f281
docs(search): explain accessible search shortcut naming
sanskarIN Nov 6, 2026
0099641
docs(hi-search): describe matching visible and accessible keyboard sh…
sanskarIN Nov 6, 2026
727fc68
test(a11y): expose rule failure summaries and offending HTML in CI lo…
sanskarIN Nov 6, 2026
69f59d2
test(a11y): capture actual search trigger text, keycap and accessibil…
sanskarIN Nov 6, 2026
07d5a3c
fix(a11y): make desktop search use its visible name and keep translat…
sanskarIN Nov 6, 2026
7883987
fix(a11y): register adaptive search accessible-name manager in VitePr…
sanskarIN Nov 6, 2026
4727f42
fix(a11y): let adaptive theme use VitePress default English/Hindi sou…
sanskarIN Nov 6, 2026
9d25d8c
test(a11y): match mobile translated label and leave desktop name to v…
sanskarIN Nov 6, 2026
320bc5c
test(a11y): check adaptive desktop/mobile search semantics and diverg…
sanskarIN Nov 6, 2026
337d500
fix(ts): narrow optional search placeholder before DOM metrics and co…
sanskarIN Nov 6, 2026
9c31ff0
fix(a11y): enable keyboard scrolling and visible focus for long Scarp…
sanskarIN Nov 6, 2026
14ea49f
test(a11y): require accessible scrollable code block and visible keyb…
sanskarIN Nov 6, 2026
106c33f
test(a11y): reject regressions that make the command block unfocusable
sanskarIN Nov 6, 2026
2756d3c
fix(a11y): expose VitePress local search overlay as translated modal …
sanskarIN Nov 6, 2026
6886378
fix(a11y): register translated modal semantics for English/Hindi sear…
sanskarIN Nov 6, 2026
e8222f5
test(a11y): assert real search popover has translated dialog role, na…
sanskarIN Nov 6, 2026
32f07b2
docs(maintenance): document targeted search button and modal accessib…
sanskarIN Nov 6, 2026
63d1202
fix(a11y): give labelled arrow and Enter key pictograms valid image s…
sanskarIN Nov 6, 2026
0c1e7c7
test(a11y): verify labelled search shortcut glyphs use valid semantics
sanskarIN Nov 6, 2026
9641c2a
fix(a11y): increase light/dark primary button color contrast to WCAG AA
sanskarIN Nov 6, 2026
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
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@ jobs:
run: npm run docs:build && npm run package:offline
- name: Check English/Hindi accessibility, CLI selector, offline search and mobile navigation in Chromium
run: npm run test:browser-offline
- name: Upload accessibility audit evidence on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: scarp-docs-accessibility-audit
path: artifacts/browser-a11y/
if-no-files-found: ignore
retention-days: 14
- name: Attach browser failure screenshot
if: failure()
uses: actions/upload-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ docs/.vitepress/dist/
artifacts/
test-results/
playwright-report/
browser-a11y/
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Official VitePress documentation for the experimental [Scarp Programming Languag

**Install the latest published compiler:** [English: Scarp v0.0.6-alpha.1](docs/guide/installation-alpha6.md) · [हिन्दी: alpha.6 इंस्टॉलेशन](docs/hi/guide/installation-alpha6.md). Windows x64, Linux x64 GNU, and Apple Silicon archives are paired with official SHA-256 checksums; the guides also provide commands for building the immutable source tag. Earlier installation walkthroughs remain historically scoped.

**Automated accessibility auditing:** The extracted offline site is checked with pinned local axe-core in 10 bilingual light/dark/search/mobile states; CI attaches per-state WCAG A/AA findings on failures. Manual screen-reader verification remains separately tracked in [Issue #65](https://github.com/scarp-lang/scarp-docs/issues/65).

**Real browser checks:** The `browser-offline` CI job runs a pinned Chromium browser against the extracted offline archive, checking keyboard navigation and search focus return, translated page switching, dark/light preference, reduced motion, mobile layout and both English/Hindi Command Finder flows. See `npm run test:browser-offline` and [maintainer guidance](docs/project/maintenance.md).

**Read the bilingual site offline:** [English offline download instructions](docs/guide/offline-docs.md) · [हिन्दी offline documentation](docs/hi/guide/offline-docs.md). A tested GitHub Actions artifact contains reproducible bundled docs, a Node.js loopback server and SHA-256 checksums. Offline docs are not compiler releases.
Expand Down
6 changes: 5 additions & 1 deletion docs/.vitepress/theme/components/ScarpCommandFinder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async function copyCommands(): Promise<void> {
<template v-if="current.supported">
<p>{{ labels.notes[current.note] }}</p>
<p class="scarp-command-finder-code-label">{{ labels.command }}</p>
<pre><code>{{ current.commands.join('\n') }}</code></pre>
<pre tabindex="0"><code>{{ current.commands.join('\n') }}</code></pre>
<button type="button" class="scarp-command-finder-copy" @click="copyCommands">
{{ copied ? labels.copied : labels.copy }}
</button>
Expand Down Expand Up @@ -126,6 +126,10 @@ async function copyCommands(): Promise<void> {
background: var(--vp-code-block-bg);
color: var(--vp-code-block-color);
}
.scarp-command-finder-result pre:focus-visible {
outline: 2px solid var(--vp-c-brand-1);
outline-offset: 2px;
}
.scarp-command-finder-copy {
margin: .75rem 0 0;
padding: .4rem .75rem;
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
--vp-c-brand-2: #2563eb;
--vp-c-brand-3: #3b82f6;
--vp-c-brand-soft: rgba(37, 99, 235, 0.1);
/* White text on VitePress's default brand-3 (#3b82f6) fails WCAG AA at 14px. */
--vp-button-brand-bg: #1d4ed8;
--vp-button-brand-hover-bg: #1e40af;
--vp-button-brand-active-bg: #1e3a8a;
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(120deg, #1d4ed8, #06b6d4);
}
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { Theme } from 'vitepress'
import './custom.css'
import PagesDeployments from './components/PagesDeployments.vue'
import { installLocalSearchFocusReturn } from './local-search-focus'
import { installAccessibleSearchTrigger } from './search-trigger-accessibility'
import { installAccessibleSearchDialog } from './search-dialog-accessibility'
import ScarpCommandFinder from './components/ScarpCommandFinder.vue'

const theme: Theme = {
Expand All @@ -12,6 +14,8 @@ const theme: Theme = {
ctx.app.component('PagesDeployments', PagesDeployments)
ctx.app.component('ScarpCommandFinder', ScarpCommandFinder)
installLocalSearchFocusReturn()
installAccessibleSearchTrigger()
installAccessibleSearchDialog()
}
}

Expand Down
48 changes: 48 additions & 0 deletions docs/.vitepress/theme/search-dialog-accessibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* VitePress 1.6 local-search popup currently renders its outer container with
* role="button", aria-haspopup="listbox" and focusable nested search/results.
* That produces nested-interactive and label-in-name WCAG axe violations.
*
* Expose the popup as a modal dialog with a translated name instead. The
* internal input, result links and original VitePress keyboard handlers remain
* unchanged. This targets VitePress 1.6.4 and should be rechecked on upgrades.
*/
let installed = false

export function installAccessibleSearchDialog(): void {
if (installed || typeof document === 'undefined' || !document.body) return
installed = true

const synchronize = (): void => {
const name = document.documentElement.lang.startsWith('hi')
? 'दस्तावेज़ खोजें'
: 'Search documentation'

for (const dialog of document.querySelectorAll<HTMLElement>('.VPLocalSearchBox')) {
if (dialog.getAttribute('role') !== 'dialog') dialog.setAttribute('role', 'dialog')
if (dialog.getAttribute('aria-modal') !== 'true') dialog.setAttribute('aria-modal', 'true')
if (dialog.getAttribute('aria-label') !== name) dialog.setAttribute('aria-label', name)

// These belong on expandable widgets, not on a modal dialog.
if (dialog.hasAttribute('aria-expanded')) dialog.removeAttribute('aria-expanded')
if (dialog.hasAttribute('aria-haspopup')) dialog.removeAttribute('aria-haspopup')
if (dialog.hasAttribute('aria-labelledby')) dialog.removeAttribute('aria-labelledby')

// VitePress applies aria-label to <kbd> arrow/Enter key glyphs.
// A bare kbd has a generic role where aria-label is prohibited;
// expose each pictogram as an explicitly labelled image instead.
for (const key of dialog.querySelectorAll<HTMLElement>('kbd[aria-label]')) {
if (key.getAttribute('role') !== 'img') key.setAttribute('role', 'img')
}
}
}

const observer = new MutationObserver(synchronize)
observer.observe(document.body, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['role', 'aria-modal', 'aria-label', 'aria-expanded', 'aria-haspopup', 'aria-labelledby']
})
synchronize()
}
56 changes: 56 additions & 0 deletions docs/.vitepress/theme/search-trigger-accessibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* VitePress 1.6 renders local-search button text and the visible "K" keycap
* without an actual separating space (textContent is "SearchK"). Its separate
* aria-label can violate WCAG "Label in Name" even when it says "Search K".
*
* When the button's visible placeholder is rendered, derive the accessible
* name from its DOM text instead of overriding it with aria-label. At narrow
* mobile sizes where the placeholder is visually hidden, keep a translated
* aria-label from that same placeholder, so an icon-only button is named.
*
* No extra network, CSS hacks, or fork of VitePress is needed.
*/
let installed = false

export function installAccessibleSearchTrigger(): void {
if (installed || typeof document === 'undefined' || !document.body) return
installed = true

const synchronizeLabels = (): void => {
for (const button of document.querySelectorAll<HTMLButtonElement>(
'button.DocSearch-Button'
)) {
const placeholder = button.querySelector<HTMLElement>(
'.DocSearch-Button-Placeholder'
)
if (!placeholder) continue
const text = placeholder.textContent?.trim()
if (!text) continue

const visible = placeholder.getClientRects().length > 0 &&
getComputedStyle(placeholder).display !== 'none' &&
getComputedStyle(placeholder).visibility !== 'hidden'

if (visible) {
// The button's child text is now the true accessible name,
// preserving the visible shortcut keycap verbatim.
if (button.hasAttribute('aria-label')) button.removeAttribute('aria-label')
} else if (button.getAttribute('aria-label') !== text) {
// On mobile, Search/Hindi text is visually hidden and must be announced.
button.setAttribute('aria-label', text)
}
}
}

// Vue renders/updates the search button after hydration and locale changes.
// Observe relevant changes; the no-op checks above prevent mutation loops.
const observer = new MutationObserver(synchronizeLabels)
observer.observe(document.body, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ['aria-label']
})
window.addEventListener('resize', synchronizeLabels, { passive: true })
synchronizeLabels()
}
2 changes: 2 additions & 0 deletions docs/guide/offline-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ The included server binds only to **127.0.0.1** (this computer). Stop with **Ctr

## What works offline?

**Automated accessibility checks:** The offline test suite scans a selection of actual English/Hindi pages and interactive states with locally bundled axe-core WCAG A/AA rules and checks 320px-wide reflow. These checks find common machine-detectable problems; they **do not replace** manual keyboard, screen-reader or contrast perception reviews.

**Automated browser verification:** the documentation CI now starts the extracted local server in headless Chromium and checks English/Hindi Command Finder interaction, real clipboard success and denied permissions, keyboard-activated local search and its empty-results state, language switching, skip-to-content, persisted dark/light appearance, mobile navigation, reduced-motion operation and the absence of external requests. The browser runs locally against the packaged archive, not the hosted GitHub Pages site. This does not constitute a complete WCAG audit.

The snapshot includes **all generated English and Hindi pages**, their local CSS/JavaScript, and the assets used by VitePress local search and the version-aware [Command Finder](/tooling/command-finder). Regular internal navigation runs against your local server. Links to GitHub, official releases, social profiles, or other external sites **still require internet**. The browser's actual keyboard/search behavior is being tracked separately in [accessibility test issue #65](https://github.com/scarp-lang/scarp-docs/issues/65); static asset checks are not equivalent to a full browser accessibility audit.
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/search-and-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Find an implemented feature, an exact CLI command or a release-specific example
3. Use the arrow keys to move through results and **Enter** to open one. Press **Esc** to close search.
4. Choose the **English / हिन्दी** language switcher to search the other translated set of pages.

The search dialog follows the current documentation locale. The **Hindi** interface includes translated search buttons, results messages and keyboard hints. It indexes Hindi documents directly; it is not automatic translation of English answers. Code identifiers and release tags remain written as in the published compiler.
The keyboard shortcut shown in the search trigger is part of its accessible name, so its text remains consistent for keyboard and screen-reader users. The search dialog follows the current documentation locale. The **Hindi** interface includes translated search buttons, results messages and keyboard hints. It indexes Hindi documents directly; it is not automatic translation of English answers. Code identifiers and release tags remain written as in the published compiler.

## Find the right version

Expand Down
2 changes: 2 additions & 0 deletions docs/hi/guide/offline-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ Server केवल **127.0.0.1** अर्थात आपके अपने

## Offline में क्या उपलब्ध है?

**Automated accessibility जाँच:** Offline test suite, local axe-core के WCAG A/AA नियमों के अनुसार English/Hindi पृष्ठों तथा interactive स्थितियों की जाँच करती है। 320px viewport पर reflow भी जाँचा जाता है। ये परीक्षण कुछ सामान्य समस्याएँ पकड़ते हैं; **manual keyboard, screen-reader और contrast जाँच का विकल्प नहीं** हैं।

**असल browser परीक्षण:** Documentation CI extracted local server को headless Chromium में चलाकर English/Hindi Command Finder, clipboard success या permission failure, keyboard से local search तथा zero-results, भाषा बदलना, skip-to-content, dark/light mode की preference, mobile navigation, reduced-motion और बाहरी network requests न होने की पुष्टि करती है। यह जाँच packaged offline archive पर होती है, hosted GitHub Pages पर नहीं। इसे सम्पूर्ण WCAG accessibility audit **नहीं** मानें।

इस package में सभी English/Hindi HTML पृष्ठ, उनके स्थानीय CSS/JavaScript, VitePress search assets और [Command Finder](/hi/tooling/command-finder) शामिल हैं। पृष्ठों के अंदर के links स्थानीय server पर खुलते हैं। **बाहरी GitHub, social profiles तथा download links** के लिए फिर भी इंटरनेट चाहिए। असली browser में keyboard, search और accessibility परीक्षण का अलग काम [Issue #65](https://github.com/scarp-lang/scarp-docs/issues/65) पर रखा गया है; static assets की पुष्टि को पूरी browser accessibility जाँच न समझें।
Expand Down
2 changes: 1 addition & 1 deletion docs/hi/guide/search-and-navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Scarp के सही keyword, compiler command या किसी प्र
3. Arrow keys से परिणाम बदलें और **Enter** से संबंधित पृष्ठ खोलें। Search बंद करने के लिए **Esc** दबाएँ।
4. **English / हिन्दी** language switcher से दूसरी भाषा के दस्तावेज़ चुनें और फिर खोजें।

हिन्दी पृष्ठों में search button, परिणाम न मिलने का संदेश और keyboard सहायता हिन्दी में दिखाई जाती है। Search हिन्दी दस्तावेज़ों को सीधे index करती है; यह English उत्तरों का अपने-आप **अनुवाद नहीं** करती। Scarp code के keywords तथा release tags मूल लिखावट में ही रहते हैं।
Search button के साथ दिखाई देने वाला **K** shortcut, उसके accessible name में भी शामिल है, ताकि screen-reader और keyboard users को एक-समान जानकारी मिले। हिन्दी पृष्ठों में search button, परिणाम न मिलने का संदेश और keyboard सहायता हिन्दी में दिखाई जाती है। Search हिन्दी दस्तावेज़ों को सीधे index करती है; यह English उत्तरों का अपने-आप **अनुवाद नहीं** करती। Scarp code के keywords तथा release tags मूल लिखावट में ही रहते हैं।

## सही compiler version चुनें

Expand Down
8 changes: 8 additions & 0 deletions docs/project/maintenance.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ The dedicated `browser-offline` Documentation CI job installs pinned Playwright/

For a local repeat, install dependencies with `npm install`, install Chromium with `npx playwright install chromium` (CI uses the same browser install on GitHub-hosted Ubuntu, which has its required system libraries), run `npm run docs:build && npm run package:offline`, then `npm run test:browser-offline`. Failure screenshots, when possible, are saved under ignored `artifacts/browser-failures/` and uploaded on CI failure. The expanded suite also checks a keyboard-opened empty search state and focus restoration on close (implemented by the small theme-level `local-search-focus.ts` manager for VitePress 1.6), translated-page switching, skip-to-content link and target, persisted appearance mode across reloads, and usable CLI controls under a reduced-motion viewport. These tests exercise selected interactive controls, not every screen-reader combination or a full WCAG conformance audit; track broader access testing in [Issue #65](https://github.com/scarp-lang/scarp-docs/issues/65). The offline distribution remains pinned to the tested docs commit, and the browser suite has no external-search backend.

## Offline accessibility rule audits

The VitePress 1.6.4 search UI needs small theme-level semantic repairs: `search-trigger-accessibility.ts` derives the desktop search control's name from visible text and labels its icon-only mobile form, while `search-dialog-accessibility.ts` presents the overlay as a properly named modal dialog rather than a button containing interactive descendants. Retest these fixes against the real browser whenever VitePress is upgraded.

The offline browser job now runs **axe-core 4.14.0** from the pinned local npm devDependency against ten extracted-site states: English/Hindi Command Finder in light and dark appearances, a narrow 320 CSS-pixel viewport, an open local-search dialog, and the English/Hindi homepages. It asserts zero automated WCAG A/AA violations, verifies that the browser did not make external network requests, and writes per-state JSON results under ignored `artifacts/browser-a11y/` (uploaded to CI on a failure). The 320px viewport checks reflow; it approximates the content width at 200% browser zoom on a 640 CSS-pixel display, **not** a test of native browser zoom or text-only zoom.

The checks run automatically within `npm run test:browser-offline` after `npm run docs:build && npm run package:offline`. If an audit reports a violation, inspect its concrete rule ID, offending selector and remediation in the uploaded JSON rather than excluding the rule. Review `incomplete` manual-review signals as well. These tests catch common machine-detectable accessibility issues but **cannot establish WCAG compliance**, screen-reader compatibility, color differentiation perception, or real-user navigation quality. Manual assistive-technology review remains tracked under [Issue #65](https://github.com/scarp-lang/scarp-docs/issues/65).

## Supported TypeScript and Vue toolchain

The docs run strict Vue SFC checks through `vue-tsc`. The tested compatibility line is **TypeScript 6.0.x** with **vue-tsc 3.3.x**. TypeScript 7.0 removes the programmatic TypeScript API expected by Vue's checker, causing `ERR_PACKAGE_PATH_NOT_EXPORTED` before actual document diagnostics run. This is an **upstream integration limitation**, not evidence that the TypeScript 7 compiler itself is defective.
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@
"typescript": "6.0.3",
"tsx": "4.23.15",
"vue-tsc": "3.3.13",
"playwright": "1.64.1"
"playwright": "1.64.1",
"axe-core": "4.14.0"
},
"engines": {
"node": ">=20.19.0"
Expand Down
Loading
Loading