openvibers/openvibe.shared Commits

Shared 4.1.5: showcase steps keep their text under the title, and four cards sit two by two below four columns (#73)

OP OpenVibers committed 29 days ago · 075eeb460c · 5 files, +21 −5

**Showcase steps line up, and four cards never fall three and one alone (openvibe-shared 4.1.5).**

**Found:** while reviewing every home page.
- **Steps:** on openvibe.events, a webhook step with one line of text had a gap under its title. The row of steps stretched the step to its taller neighbour, and the step's own grid spread the extra height between title and text.
- **Four cards:** a four-card grid in a section 750–1035 px wide fell three and one alone. This happened on Reviews and Coupons, and on any site whose column is narrower than four 250 px cards.

**Change (`showcase.js`; `showcase.css` regenerated by `scripts/build-styles.js`):**
- `.sc-steps li` gets `align-content:start`, so a step's rows stay at the top.
- `.sc-sec` becomes a size container (`container-type:inline-size`). A grid of exactly four cards in a section 520–1035 px wide shows two by two. Wider sections keep four across, and narrow ones keep one column.

**Tests:** `showcase.test.js` checks all three rules. `build-styles --check` keeps the served file equal to the module.

After the merge: tag v4.1.5, then a pin round (pin-bump.js). Sites carrying a local copy of these rules (Events, Coupons) can drop it after their bump.

Changes

ddiff --git a/CHANGELOG.md b/CHANGELOG.md
iindex d1fcd71..9b043cf 100644
---- a/CHANGELOG.md
++++ b/CHANGELOG.md
@@@ -4,6 +4,15 @@ 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.1.5 — 2026-10-15
++
++- **Steps line up.** In a row of steps, a step whose text is one line had a gap under its title: the row stretched it to
++ its taller neighbour's height, and its own grid spread the extra space between title and text. `.sc-steps li` now
++ keeps its rows at the top (`align-content:start`).
++- **Four cards never fall three and one.** A section is now a size container (`container-type:inline-size`), and a grid
++ of exactly four cards in a section narrower than four columns (520–1035 px) sits two by two. Wider sections keep four
++ across; phones keep one column.
++
## 4.1.4 — 2026-10-14
- **A `play` glyph for OpenVibe.Play.** Play (achievements and leaderboards for every OpenVibe game) gets its own icon:
ddiff --git a/package.json b/package.json
iindex 681cb27..d33ae7c 100644
---- a/package.json
++++ b/package.json
@@@ -1,6 +1,6 @@
{
"name": "openvibe-shared",
-- "version": "4.1.4",
++ "version": "4.1.5",
"description": "Shared navbar, footer, themes, icons, legal pages, SEO, auth and analytics for every OpenVibe site",
"main": "index.js",
"bin": {
ddiff --git a/showcase.css b/showcase.css
iindex 9450c4a..65bf37a 100644
---- a/showcase.css
++++ b/showcase.css
@@@ -14,10 +14,11 @@
.sc-btn.sc-primary:hover{transform:translateY(-1px);filter:brightness(1.06)}
.sc-note{margin:14px 0 0;font-size:13.5px;color:var(--text-muted,#7386a3)}
.sc-note a{color:var(--accent-light,#60a5fa)}
--.sc-sec{margin-top:56px}
++.sc-sec{margin-top:56px;container-type:inline-size}
.sc-sec>h2,.sc-cta>h2{font-size:clamp(22px,2.6vw,30px);letter-spacing:-.02em;margin:0 0 6px}
.sc-lede{color:var(--text-secondary,#96a7c2);margin:0 0 18px;max-width:760px;line-height:1.55}
.sc-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
++@container (min-width:520px) and (max-width:1035px){.sc-grid:has(>:nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:900px){.sc-grid:has(>:nth-child(5):last-child),.sc-grid:has(>:nth-child(6):last-child),.sc-grid:has(>:nth-child(9):last-child){grid-template-columns:repeat(3,minmax(0,1fr))}}
.sc-card{display:flex;gap:14px;align-items:flex-start;padding:16px;border-radius:16px;border:1px solid var(--border,rgba(255,255,255,.08));background:var(--bg-secondary,#101828);color:inherit;text-decoration:none;transition:border-color .15s,transform .15s}
a.sc-card:hover,a.sc-card:focus-visible{border-color:var(--accent,#3b82f6);transform:translateY(-2px);outline:0}
@@@ -25,7 +26,7 @@ a.sc-card:hover,a.sc-card:focus-visible{border-color:var(--accent,#3b82f6);trans
.sc-ic.sc-ic-ov{background:none;border-radius:50%}
.sc-card b{display:block;font-size:15.5px}.sc-card small{display:block;margin-top:3px;color:var(--text-secondary,#96a7c2);font-size:13.5px;line-height:1.45}
.sc-steps{list-style:none;margin:0;padding:0;counter-reset:sc;display:grid;gap:14px 24px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
--.sc-steps li{counter-increment:sc;display:grid;grid-template-columns:34px 1fr;column-gap:12px;align-items:baseline}
++.sc-steps li{counter-increment:sc;display:grid;grid-template-columns:34px 1fr;column-gap:12px;align-items:baseline;align-content:start}
.sc-steps li::before{content:counter(sc);grid-row:span 2;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;font-weight:800;font-size:13px;color:var(--accent-light,#60a5fa);border:1px solid var(--border-light,#2c3d5c);background:var(--bg-secondary,#101828)}
.sc-steps b{font-size:15.5px}.sc-steps b a{color:inherit;text-decoration:underline;text-decoration-color:var(--accent,#3b82f6);text-underline-offset:3px}
.sc-steps span{color:var(--text-secondary,#96a7c2);font-size:14px;line-height:1.5}
ddiff --git a/showcase.js b/showcase.js
iindex bfdf88e..232e2e4 100644
---- a/showcase.js
++++ b/showcase.js
@@@ -180,10 +180,11 @@ const CSS = `
.sc-btn.sc-primary:hover{transform:translateY(-1px);filter:brightness(1.06)}
.sc-note{margin:14px 0 0;font-size:13.5px;color:var(--text-muted,#7386a3)}
.sc-note a{color:var(--accent-light,#60a5fa)}
--.sc-sec{margin-top:56px}
++.sc-sec{margin-top:56px;container-type:inline-size}
.sc-sec>h2,.sc-cta>h2{font-size:clamp(22px,2.6vw,30px);letter-spacing:-.02em;margin:0 0 6px}
.sc-lede{color:var(--text-secondary,#96a7c2);margin:0 0 18px;max-width:760px;line-height:1.55}
.sc-grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fill,minmax(250px,1fr))}
++@container (min-width:520px) and (max-width:1035px){.sc-grid:has(>:nth-child(4):last-child){grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (min-width:900px){.sc-grid:has(>:nth-child(5):last-child),.sc-grid:has(>:nth-child(6):last-child),.sc-grid:has(>:nth-child(9):last-child){grid-template-columns:repeat(3,minmax(0,1fr))}}
.sc-card{display:flex;gap:14px;align-items:flex-start;padding:16px;border-radius:16px;border:1px solid var(--border,rgba(255,255,255,.08));background:var(--bg-secondary,#101828);color:inherit;text-decoration:none;transition:border-color .15s,transform .15s}
a.sc-card:hover,a.sc-card:focus-visible{border-color:var(--accent,#3b82f6);transform:translateY(-2px);outline:0}
@@@ -191,7 +192,7 @@ a.sc-card:hover,a.sc-card:focus-visible{border-color:var(--accent,#3b82f6);trans
.sc-ic.sc-ic-ov{background:none;border-radius:50%}
.sc-card b{display:block;font-size:15.5px}.sc-card small{display:block;margin-top:3px;color:var(--text-secondary,#96a7c2);font-size:13.5px;line-height:1.45}
.sc-steps{list-style:none;margin:0;padding:0;counter-reset:sc;display:grid;gap:14px 24px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}
--.sc-steps li{counter-increment:sc;display:grid;grid-template-columns:34px 1fr;column-gap:12px;align-items:baseline}
++.sc-steps li{counter-increment:sc;display:grid;grid-template-columns:34px 1fr;column-gap:12px;align-items:baseline;align-content:start}
.sc-steps li::before{content:counter(sc);grid-row:span 2;width:30px;height:30px;border-radius:50%;display:grid;place-items:center;font-weight:800;font-size:13px;color:var(--accent-light,#60a5fa);border:1px solid var(--border-light,#2c3d5c);background:var(--bg-secondary,#101828)}
.sc-steps b{font-size:15.5px}.sc-steps b a{color:inherit;text-decoration:underline;text-decoration-color:var(--accent,#3b82f6);text-underline-offset:3px}
.sc-steps span{color:var(--text-secondary,#96a7c2);font-size:14px;line-height:1.5}
ddiff --git a/test/showcase.test.js b/test/showcase.test.js
iindex 5371a6a..aafde09 100644
---- a/test/showcase.test.js
++++ b/test/showcase.test.js
@@@ -38,6 +38,11 @@ execFileSync(process.execPath, [require('path').join(__dirname, '..', 'scripts',
const files = require('../files');
assert.ok(files.isBrowserFile('showcase.css') && files.STYLES.includes('showcase.css') && !files.BROWSER.includes('showcase.css'), 'served as a stylesheet, not a script');
assert.strictEqual(sc.STYLESHEET, 'showcase.css');
++// A step keeps its text under its title when its neighbour in the row is taller (rows of the step's own grid stay at the
++// top), and four cards in a section too narrow for four across sit two by two, never three and one alone.
++assert.ok(/\.sc-steps li\{[^}]*align-content:start/.test(sc.CSS), 'steps do not stretch their rows');
++assert.ok(/\.sc-sec\{[^}]*container-type:inline-size/.test(sc.CSS), 'a section is the container its grid asks about');
++assert.ok(/@container \(min-width:520px\) and \(max-width:1035px\)\{\.sc-grid:has\(>:nth-child\(4\):last-child\)\{grid-template-columns:repeat\(2,minmax\(0,1fr\)\)\}\}/.test(sc.CSS), 'four cards: two by two below four columns');
// A ring icon fills the 40 px slot with no second tile; a Font Awesome icon keeps the tinted tile at its own size.
{
const ring = sc.features({ items: [{ icon: 'ov:live', title: 'Live' }, { icon: 'fa-code', title: 'Code' }] });