openvibers/openvibe.shared Commits

Tabs below the fold no longer scroll the page to themselves (4.77.1) (#230)

OP OpenVibers committed 12 days ago · 7e518aba46 · 6 files, +23 −5

kit-web.js kept the open tab in view with scrollIntoView({ block: 'nearest' }), which scrolled the
page to a tab strip below the fold. Only the strip moves now, sideways.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex 740ec43..b16cceb 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,12 @@ 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).
++## 4.77.1 — 2026-11-01
++
++- **Tabs below the fold no longer scroll the page to themselves.** kit-web.js kept the open tab in view with
++ `scrollIntoView({ block: 'nearest' })`, which also scrolled the page when a tab strip sat below the fold (a settings
++ section lower down, openvibe.design/kit's samples). Only the strip moves now, sideways.
++
## 4.77.0 — 2026-11-01
- **Talk to Vibe in any browser** (plan L48 V2). Where the browser has no speech recognition of its own (Firefox),
ddiff --git a/README.md b/README.md
iindex 3a7455c..c2beac5 100644
---- a/README.md
++++ b/README.md
@@@ -2,7 +2,7 @@
> Versioned UI, the OpenVibe Frame (the navbar, footer and "shipped" views every site sits in), SEO, legal and release-client packages every OpenVibe site renders.
--**Status:** alpha, v4.77.0 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
++**Status:** alpha, v4.77.1 (every release is a tag; see [CHANGELOG.md](CHANGELOG.md)). Every deployed
consumer installs a tagged release and none keeps a vendored copy
([docs/migration-plan.md](docs/migration-plan.md) is done); each repository's `package.json` names
the tag it pins.
ddiff --git a/STATUS.json b/STATUS.json
iindex 52a20e0..9b785b0 100644
---- a/STATUS.json
++++ b/STATUS.json
@@@ -4,7 +4,7 @@
"code": true,
"wave": "W2",
"package": "openvibe-shared",
-- "version": "4.77.0",
++ "version": "4.77.1",
"plan": "OpenVibe End-to-End Realignment & Implementation Plan, revision 3 (20 Sep 2026)",
"deployed": false,
"deployment": "none of its own: a package released as git tags; OpenVibe.Network serves /shared/* from its pinned tag, and a site can serve its own pinned copy (openvibe-shared/serve)",
ddiff --git a/kit-web.js b/kit-web.js
iindex 78f8d9c..e1f2a7e 100644
---- a/kit-web.js
++++ b/kit-web.js
@@@ -143,7 +143,14 @@
d.addEventListener('click', (e) => { if (e.target === d || (e.target.closest && e.target.closest('[data-ovk-close]'))) d.close(); });
});
// Tabs that scroll sideways keep the open one in view.
-- all('.ovk-tabs [aria-current="page"]').forEach((a) => { if (once(a, 'ovkTab') && a.scrollIntoView) a.scrollIntoView({ block: 'nearest', inline: 'center' }); });
++ // Only the strip moves, sideways: scrollIntoView would also scroll the page to a strip below the fold.
++ all('.ovk-tabs [aria-current="page"]').forEach((a) => {
++ if (!once(a, 'ovkTab')) return;
++ const strip = a.closest('.ovk-tabs');
++ if (!strip || strip.scrollWidth <= strip.clientWidth) return;
++ const ar = a.getBoundingClientRect(), sr = strip.getBoundingClientRect();
++ strip.scrollLeft += (ar.left + ar.width / 2) - (sr.left + sr.width / 2);
++ });
// Rails: ‹ › move a page of cards where there is a pointer to click them; each hides at its end.
const fine = typeof matchMedia === 'function' && matchMedia('(hover: hover) and (pointer: fine)').matches;
const still = () => (typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches) || document.documentElement.getAttribute('data-ov-motion') === 'reduced';
ddiff --git a/package.json b/package.json
iindex c993d9e..9118c32 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.77.0",
++ "version": "4.77.1",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/test/kit-web-chrome.test.js b/test/kit-web-chrome.test.js
iindex 997d9a9..9796e9f 100644
---- a/test/kit-web-chrome.test.js
++++ b/test/kit-web-chrome.test.js
@@@ -23,8 +23,11 @@ const BODY = kit.snippet({ code: 'hello', id: 'sn' })
+ kit.action({ action: '/gone', label: 'Delete', confirm: 'Delete it?' })
+ kit.tool({ head: kit.toolHead({ title: 'Whois Lookup', crumb: { label: 'Network tools' } }),
input: kit.toolInput({ name: 'q', label: 'Domain', button: 'Look up', tries: { items: [{ value: 'openvibe.network' }] } }) });
++// Tabs far below the fold, the open one off to the right of a narrow strip (4.77.1: scrollIntoView moved the page).
++const LOW_TABS = `<div style="height:2400px"></div><div style="width:240px"><style>.ovk-tabs{display:flex;overflow-x:auto;white-space:nowrap}.ovk-tabs a{flex:none;padding:0 18px}</style>${
++ kit.tabs({ base: '/s', active: 'h', items: ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'].map((k) => ({ key: k, label: `Section ${k}` })) })}</div>`;
const PAGE = `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Kit</title><link rel="icon" href="data:,"></head>
--<body>${BODY}<script>
++<body>${BODY}${LOW_TABS}<script>
window.__asked = []; window.confirm = (m) => { window.__asked.push(m); return false; };
window.__sent = [];
document.addEventListener('submit', (e) => { if (!e.defaultPrevented) { e.preventDefault(); window.__sent.push([e.target.getAttribute('action'), new FormData(e.target).get('q')]); } });
@@@ -47,6 +50,8 @@ const server = http.createServer((req, res) => {
const p = await h.openPage(browser, context, { width: 390 });
await p.goto(`${base}/`, { maxMs: 5000 });
await wait(200);
++ assert.strictEqual(await p.evaluate('Math.round(scrollY)'), 0, 'tabs below the fold never scroll the page to themselves');
++ assert.ok(await p.evaluate("document.querySelector('.ovk-tabs').scrollLeft") > 0, 'but their strip shows the open tab');
const hasClipboard = await p.evaluate('!!navigator.clipboard');
if (hasClipboard) assert.strictEqual(await p.evaluate("document.querySelector('[data-ovk-copy]').hidden"), false, 'the copy button shows where the clipboard works');
await p.evaluate("document.querySelector('[data-ovk-sheet]').click()");