openvibers/openvibe.live Commits

Icons stay put: the go-live guide's root is .ovg-root, never the glyph icons' .ovg (#493)

OP OpenVibers committed 12 days ago · 60bffb17b8 · 4 files, +39 −4

openvibe-shared's ov-glyphs.js draws every icon as <i class="fa-… ovg">. The guide's stylesheet styled the bare
class .ovg as its full-screen root (position: fixed; inset: 0; z-index: 21000; display: none), so once it loaded,
a couple of seconds in for signed-in streamers, about 200 of the home page's icons vanished and the rest were pinned
to the top-left corner above everything: the play button in the corner and the floating squares the owner saw.

test/glyph-class.test.js keeps any rule or element of Live's own off the bare class.

Changes

ddiff --git a/public/css/ov-guide.css b/public/css/ov-guide.css
iindex 0bd59b73..155dca64 100644
---- a/public/css/ov-guide.css
++++ b/public/css/ov-guide.css
@@@ -1,6 +1,8 @@
/* ── Go Live setup wizard (public/js/golive-wizard.js) ── */
--.ovg { position: fixed; inset: 0; z-index: 21000; display: none; }
--.ovg.is-open { display: block; }
++/* The root is .ovg-root, never .ovg: openvibe-shared's ov-glyphs.js marks every icon it draws with class "ovg", and a
++ bare .ovg rule here hid every icon on the page and pinned the rest to the top-left corner (the owner, 2026-11-01). */
++.ovg-root { position: fixed; inset: 0; z-index: 21000; display: none; }
++.ovg-root.is-open { display: block; }
body.ovg-lock { overflow: hidden; }
.ovg-scrim { position: absolute; inset: 0; background: rgba(4,5,10,0.72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); animation: ovgFade .25s ease; }
.ovg-card { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); width: min(720px, calc(100vw - 24px)); max-height: calc(100vh - 32px); display: flex; flex-direction: column;
ddiff --git a/public/js/app.js b/public/js/app.js
iindex 5bf7fae7..72be7914 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -824,7 +824,7 @@ function ovClearStrandedOverlays() {
document.querySelectorAll(sel).forEach(el => { try { el.remove(); } catch { /* */ } });
}
// The scroll lock outliving its modal is the other way a page goes dead.
-- try { if (!document.querySelector('.ovg.is-open')) document.body.classList.remove('ovg-lock'); } catch { /* */ }
++ try { if (!document.querySelector('.ovg-root.is-open')) document.body.classList.remove('ovg-lock'); } catch { /* */ }
}
/**
ddiff --git a/public/js/ov-guide.js b/public/js/ov-guide.js
iindex b1d3b6c7..9cd4d05b 100644
---- a/public/js/ov-guide.js
++++ b/public/js/ov-guide.js
@@@ -41,7 +41,7 @@
function shell() {
let root = $('#ovg');
if (root) return root;
-- root = document.createElement('div'); root.id = 'ovg'; root.className = 'ovg';
++ root = document.createElement('div'); root.id = 'ovg'; root.className = 'ovg-root'; // never 'ovg': the glyph icons' class
root.innerHTML = `<div class="ovg-scrim"></div>
<div class="ovg-card" role="dialog" aria-modal="true">
<button type="button" class="ovg-close" aria-label="Close"><i class="fa-solid fa-xmark"></i></button>
ddiff --git a/test/glyph-class.test.js b/test/glyph-class.test.js
nnew file mode 100644
iindex 00000000..b39e0276
---- /dev/null
++++ b/test/glyph-class.test.js
@@@ -0,0 +1,33 @@
++'use strict';
++/**
++ * openvibe-shared's ov-glyphs.js (loaded by the shared navbar on every page that uses Font Awesome) draws each icon as
++ * <i class="fa-solid fa-… ovg"><svg>…</svg></i>. A rule of Live's own on the bare class .ovg styles every one of those
++ * icons too: the go-live guide's root was .ovg { position: fixed; inset: 0; display: none }, so once its stylesheet
++ * loaded (a couple of seconds in, for signed-in streamers) every icon vanished and the rest were pinned to the
++ * top-left corner (the owner, 2026-11-01: "broken icons… the play button in the top left… cubes"). Live's own classes
++ * may start with "ovg-" (the guide's .ovg-quest, .ovg-root …) but never be exactly "ovg".
++ */
++const assert = require('assert');
++const fs = require('fs');
++const path = require('path');
++
++const ROOT = path.join(__dirname, '..', 'public');
++const files = [];
++(function walk(dir) {
++ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
++ const p = path.join(dir, e.name);
++ if (e.isDirectory()) { if (e.name !== 'vendor') walk(p); } else if (/\.(css|js|html)$/.test(e.name)) files.push(p);
++ }
++})(ROOT);
++
++const BARE_SELECTOR = /(^|[\s,}>~+(])\.ovg(?![\w-])/; // .ovg as a whole class in a selector
++const BARE_CLASS = /(class(Name)?\s*=\s*['"]|classList\.(add|toggle|contains)\(\s*['"])ovg(?=['"\s])/; // class="ovg …", className = 'ovg'
++const offenders = [];
++for (const f of files) {
++ const text = fs.readFileSync(f, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '');
++ text.split('\n').forEach((line, i) => {
++ if (BARE_SELECTOR.test(line) || BARE_CLASS.test(line)) offenders.push(`${path.relative(ROOT, f)}:${i + 1}: ${line.trim().slice(0, 120)}`);
++ });
++}
++assert.deepStrictEqual(offenders, [], `the class "ovg" belongs to the shared glyph icons; use .ovg-root or another ovg-… name:\n${offenders.join('\n')}`);
++console.log(` ✓ no rule or element of Live's own uses the glyph icons' class "ovg" (${files.length} files)`);