openvibers/openvibe.zone Commits

Zone: code blocks are keyboard-focusable (axe scrollable-region-focusable) (#11)

OP OpenVibers committed 29 days ago · 21bc65cda2 · 3 files, +5 −4

An axe scan (WCAG 2.1 AA) of every OpenVibe home page at 390 px found code blocks that scroll sideways but cannot be reached by keyboard (`scrollable-region-focusable`, serious): the AWS CLI and rclone samples on the home page.

Every `<pre>` now has `tabindex="0"`: the home page samples and Markdown fences. `test/zone-api.test.js` checks the home page has no unfocusable `<pre>`.

Changes

ddiff --git a/server/http/pages.js b/server/http/pages.js
iindex 1c8a9c2..a1613ea 100644
---- a/server/http/pages.js
++++ b/server/http/pages.js
@@@ -88,11 +88,11 @@ ${raw(showcase.steps({
],
}))}
<section class="zn-section" aria-labelledby="zn-try"><h2 id="zn-try">With the tools you have</h2>
--<pre class="zn-code"><code>aws configure set aws_access_key_id ovk_…
++<pre tabindex="0" class="zn-code"><code>aws configure set aws_access_key_id ovk_…
aws configure set aws_secret_access_key …
aws --endpoint-url ${s3} s3 cp ./site s3://assets/ --recursive
aws --endpoint-url ${s3} s3 ls s3://assets/</code></pre>
--<pre class="zn-code"><code>rclone config create oz s3 provider Other endpoint ${s3} \\
++<pre tabindex="0" class="zn-code"><code>rclone config create oz s3 provider Other endpoint ${s3} \\
access_key_id ovk_… secret_access_key … no_check_bucket true
rclone sync ./backups oz:backups</code></pre>
<p class="zn-muted">Large files go up in parts, as these tools do by default: each part up to 100 MB, an object up to 20 GB.</p>
ddiff --git a/server/render/markdown.js b/server/render/markdown.js
iindex f0ab0e7..e924991 100644
---- a/server/render/markdown.js
++++ b/server/render/markdown.js
@@@ -37,7 +37,7 @@ function markdown(src, { headingOffset = 1 } = {}) {
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (fence) {
-- if (/^```/.test(line)) { out.push(`<pre><code>${esc(fence.join('\n'))}</code></pre>`); fence = null; } else fence.push(line);
++ if (/^```/.test(line)) { out.push(`<pre tabindex="0"><code>${esc(fence.join('\n'))}</code></pre>`); fence = null; } else fence.push(line);
continue;
}
if (/^```/.test(line)) { flushPara(); flushList(); fence = []; continue; }
@@@ -72,7 +72,7 @@ function markdown(src, { headingOffset = 1 } = {}) {
flushList();
para.push(line.trim());
}
-- if (fence) out.push(`<pre><code>${esc(fence.join('\n'))}</code></pre>`);
++ if (fence) out.push(`<pre tabindex="0"><code>${esc(fence.join('\n'))}</code></pre>`);
flushPara(); flushList();
return out.join('\n');
}
ddiff --git a/test/zone-api.test.js b/test/zone-api.test.js
iindex a12b016..5de2223 100644
---- a/test/zone-api.test.js
++++ b/test/zone-api.test.js
@@@ -267,6 +267,7 @@ function fakeMedia(network) {
assert.strictEqual(r.status, 401);
r = await t.get('/');
assert.ok(r.status === 200 && r.text.includes('s3.openvibe.media') && r.text.includes('Private storage zones'));
++ assert.ok(!/<pre(?![^>]*tabindex)[\s>]/.test(r.text), 'every <pre> is focusable (axe scrollable-region-focusable)');
r = await t.get('/docs');
assert.ok(r.status === 200 && r.text.includes('zone.zone.create'));
// The S3 list matches Media's surface: copies are supported (Media#113); ACLs, policies and versioning are not.