openvibers/openvibe.shared Commits

Shared 2.13.1: showcase code samples are keyboard-focusable and named (axe scrollable-region-focusable) (#31)

OP OpenVibers committed 8 Oct 2026 · 75dfc36289 · 5 files, +11 −5

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 4d46e26..20c0ca1 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,7 +4,11 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
breaking change to any exported module, browser global or served file name is a new major.
A release is the git tag `vX.Y.Z`; consumers pin the tag's tarball (see README).
--## Unreleased
++## 2.13.1 — 2026-10-08
++
++- `showcase` code samples are a focus stop with a name (`<pre tabindex="0" aria-label="…">`): a sample that scrolls
++ sideways could not be reached from the keyboard (axe `scrollable-region-focusable`, serious; ovhost browser-check
++ on ai.openvibe.services and openvibe.events, 2026-10-08).
- `scripts/pin-bump.js` (plan T1, the pin bot): after a release of openvibe-contracts, -shared, -sdk or -publishing,
`node scripts/pin-bump.js <library> <tag> [--repos A,B] [--apply]` opens one PR per OpenVibers repository that pins
ddiff --git a/package-lock.json b/package-lock.json
iindex 4019813..9d2fba9 100644
---- a/package-lock.json
++++ b/package-lock.json
@@@ -1,12 +1,12 @@
{
"name": "openvibe-shared",
-- "version": "2.13.0",
++ "version": "2.13.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openvibe-shared",
-- "version": "2.13.0",
++ "version": "2.13.1",
"license": "MIT",
"dependencies": {
"jsonwebtoken": "^9.0.0"
ddiff --git a/package.json b/package.json
iindex 180c82d..165c858 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "2.13.0",
++ "version": "2.13.1",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/showcase.js b/showcase.js
iindex b07bfc5..8479028 100644
---- a/showcase.js
++++ b/showcase.js
@@@ -73,7 +73,7 @@ function steps({ id: sid, title, lede, items } = {}) {
function code({ id: sid, title, lede, samples } = {}) {
if (!has(samples)) return '';
const h = `sc-${slug(title)}`;
-- return `<section class="sc-sec"${id(sid)}${labelled(title, h)}>${head(title, lede, h)}<div class="sc-code">${samples.map((c, i) => `<figure><figcaption>${esc(c.label || c.lang || `Sample ${i + 1}`)}</figcaption><pre><code${c.lang ? ` class="language-${esc(c.lang)}"` : ''}>${esc(String(c.code || '').replace(/^\n+|\s+$/g, ''))}</code></pre></figure>`).join('')}</div></section>`;
++ return `<section class="sc-sec"${id(sid)}${labelled(title, h)}>${head(title, lede, h)}<div class="sc-code">${samples.map((c, i) => `<figure><figcaption>${esc(c.label || c.lang || `Sample ${i + 1}`)}</figcaption><pre tabindex="0" aria-label="${esc(c.label || c.lang || `Sample ${i + 1}`)}"><code${c.lang ? ` class="language-${esc(c.lang)}"` : ''}>${esc(String(c.code || '').replace(/^\n+|\s+$/g, ''))}</code></pre></figure>`).join('')}</div></section>`;
}
/** A live demo: the product itself in a frame, or the caller's own markup. */
ddiff --git a/test/showcase.test.js b/test/showcase.test.js
iindex c74b7f4..5371a6a 100644
---- a/test/showcase.test.js
++++ b/test/showcase.test.js
@@@ -27,6 +27,8 @@ assert.ok(p.includes('<p class="sc-price">Free</p>') && p.includes('$5<small> /
const l = sc.limits({ title: 'Limits', columns: ['Free'], rows: [{ label: 'Requests per minute', values: [60] }], source: 'https://openvibe.network/limits.json' });
assert.ok(l.includes('<td>60</td>') && l.includes('href="https://openvibe.network/limits.json"'), 'limits cite their source');
const code = sc.code({ title: 'Code', samples: [{ label: 'Node', lang: 'js', code: '\n<script>x</script>\n' }] });
++// A code sample scrolls sideways, so it is a focus stop with a name (axe scrollable-region-focusable).
++assert.ok(code.includes('<pre tabindex="0" aria-label="Node">'), 'code samples are keyboard-focusable and named');
assert.ok(code.includes('class="language-js"') && code.includes('&lt;script&gt;x&lt;/script&gt;</code>'), 'code is escaped and trimmed');
const d = sc.demo({ title: 'Try it', iframe: { src: 'https://x.example/?a=1&b=2', height: 5000 } });
assert.ok(d.includes('loading="lazy"') && d.includes('height:900px') && d.includes('&amp;b=2'), 'a demo frame is lazy and bounded');