openvibers/openvibe.shared Commits
Shared 2.13.1: showcase code samples are keyboard-focusable and named (axe scrollable-region-focusable) (#31)
Changes
@@@ -4,7 +4,11 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semvebreaking 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@@@ -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"@@@ -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": {@@@ -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. */@@@ -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('<script>x</script></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('&b=2'), 'a demo frame is lazy and bounded');
@@ -4,7 +4,11 @@ All notable changes to `openvibe-shared`. Versions follow [semver](https://semve
@@ -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.
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).
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,
- `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
`node scripts/pin-bump.js <library> <tag> [--repos A,B] [--apply]` opens one PR per OpenVibers repository that pins
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "2.13.0",
+ "version": "2.13.1",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-shared",
"name": "openvibe-shared",
- "version": "2.13.0",
+ "version": "2.13.1",
"license": "MIT",
"license": "MIT",
"dependencies": {
"dependencies": {
"jsonwebtoken": "^9.0.0"
"jsonwebtoken": "^9.0.0"
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-shared",
"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",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"main": "index.js",
"bin": {
"bin": {
@@ -73,7 +73,7 @@ function steps({ id: sid, title, lede, items } = {}) {
@@ -73,7 +73,7 @@ function steps({ id: sid, title, lede, items } = {}) {
function code({ id: sid, title, lede, samples } = {}) {
function code({ id: sid, title, lede, samples } = {}) {
if (!has(samples)) return '';
if (!has(samples)) return '';
const h = `sc-${slug(title)}`;
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. */
/** A live demo: the product itself in a frame, or the caller's own markup. */
@@ -27,6 +27,8 @@ assert.ok(p.includes('<p class="sc-price">Free</p>') && p.includes('$5<small> /
@@ -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' });
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');
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' }] });
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('<script>x</script></code>'), 'code is escaped and trimmed');
assert.ok(code.includes('class="language-js"') && code.includes('<script>x</script></code>'), 'code is escaped and trimmed');
const d = sc.demo({ title: 'Try it', iframe: { src: 'https://x.example/?a=1&b=2', height: 5000 } });
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('&b=2'), 'a demo frame is lazy and bounded');
assert.ok(d.includes('loading="lazy"') && d.includes('height:900px') && d.includes('&b=2'), 'a demo frame is lazy and bounded');