openvibers/openvibe.live Commits

Hero: chip alignment, shipped line moved up, and another pass at the blank buttons

OP OpenVibers committed 16 Sept 2026 · cc43ff4334 · 4 files, +50 −10

Chips: the delta sat inline beside the number, so a wide pairing wrapped to a second line, pushed
the label down and left that chip taller than its neighbours — three rows, none of them lining
up. Three columns now: icon, number over label, delta pinned right and vertically centred. A chip
with no delta collapses that column to zero, so every chip is the same shape, nothing wraps, and
nothing can change a chip's height.

Hero buttons going blank while scrolling: the previous fix made one thing worse. Gating
`will-change: transform` on the viewport class meant the browser built and threw away a
compositor layer per button every time the row scrolled in or out — precisely when the content
was disappearing. It is gone entirely; the animation is a transform on a small element and does
not need the hint. Also dropped the now-pointless `isolation: isolate`, and the hover ring — a
conic gradient behind an xor mask, one of the most expensive things a phone can paint — is no
longer built at all until a pointer is actually on the button, which on a phone is never.

The shipped-commit line moved from the bottom of the page to under the philosophy and legal row
in the hero, so the proof sits next to the promise.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01CMxJm1dB8rdsaMSgNjVa82

Changes

ddiff --git a/public/css/home-fx.css b/public/css/home-fx.css
iindex a2683acc..549b62f9 100644
---- a/public/css/home-fx.css
++++ b/public/css/home-fx.css
@@@ -24,7 +24,7 @@
padding: clamp(6px, 1.2vw, 8px) clamp(14px, 2.4vw, 20px) clamp(6px, 1.2vw, 8px) 6px;
border-radius: 999px; min-height: clamp(44px, 7.2vw, 52px); width: 100%;
font: inherit; font-weight: 700; font-size: clamp(0.88rem, 1.6vw, 0.98rem); letter-spacing: 0.01em;
-- color: var(--text-primary); text-decoration: none; cursor: pointer; isolation: isolate; overflow: hidden;
++ color: var(--text-primary); text-decoration: none; cursor: pointer; overflow: hidden;
background:
linear-gradient(180deg, rgba(255,255,255,0.07), rgba(255,255,255,0) 46%),
color-mix(in srgb, var(--c) 10%, var(--bg-card));
@@@ -51,14 +51,18 @@
}
/* home-fx.js adds .fx-live when the row scrolls into view and restarts it, so the wave always
plays when someone arrives at the buttons instead of running unseen on a page-load clock. */
--#page-home .hero-cta.fx-live .hb::before { animation-play-state: running; will-change: transform; }
++#page-home .hero-cta.fx-live .hb::before { animation-play-state: running; }
#page-home .hero-cta.fx-live .hb-ico { animation-play-state: running; }
.hb::after {
content: ''; position: absolute; inset: -1px; border-radius: inherit; z-index: 0; padding: 1px;
background: conic-gradient(from var(--a, 0deg), transparent 0 70%, var(--c) 85%, transparent 100%);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor; mask-composite: exclude; opacity: 0; transition: opacity 0.25s;
++ display: none;
}
++/* Only built when a pointer is actually on the button — there are no hover states on a phone,
++ so this never costs a touch device anything. */
++.hb:hover::after, .hb:focus-visible::after { display: block; }
.hb-ico {
width: clamp(30px, 5vw, 34px); height: clamp(30px, 5vw, 34px); border-radius: 50%; flex: none;
display: grid; place-items: center; font-size: clamp(0.82rem, 1.5vw, 0.92rem); color: var(--c);
ddiff --git a/public/css/style.css b/public/css/style.css
iindex 8b67c139..2d783907 100644
---- a/public/css/style.css
++++ b/public/css/style.css
@@@ -15085,3 +15085,39 @@ a .arena-thread:hover { color: var(--text-primary); background: color-mix(in srg
min-height: 0 !important;
transition: height 0.42s cubic-bezier(.2, 1, .3, 1);
}
++
++
++/* ── Stat chip alignment ─────────────────────────────────────────────────────
++ The delta used to sit inline beside the number, so a wide pairing ("70,177" next to "+1.7k 7d")
++ wrapped to a second line, pushed the label down, and left that chip taller than its neighbours
++ — three rows of chips, none of them lining up.
++
++ Three columns instead: icon, then the number over its label, then the delta pinned to the right
++ edge and vertically centred. Every chip is now the same shape whether it has a delta or not,
++ because an absent delta simply collapses its column to zero. Nothing wraps, so nothing can
++ change a chip's height. */
++.hero-stat {
++ display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
++ align-items: center; gap: clamp(6px, 1vw, 10px);
++}
++.hero-stat-meta { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
++.hero-stat-num { white-space: nowrap; line-height: 1.15; }
++.hero-stat-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2; }
++.hero-stat-delta { justify-self: end; align-self: center; flex: none; }
++/* The sparkline chip is a single full-width cell, not a three-column one. */
++.hero-stat--spark { display: flex; }
++
++/* The shipped-commit line now sits under the philosophy row in the hero, so it reads as a
++ footnote to "built in the open" rather than as something bolted to the bottom of the page. */
++#page-home .hero-latest {
++ display: inline-flex; align-items: center; gap: 8px; max-width: min(560px, 92vw);
++ margin: 0 auto 12px; padding: 6px 14px; border-radius: 999px;
++ font-size: 0.76rem; color: var(--text-muted); text-decoration: none;
++ background: color-mix(in srgb, var(--accent) 7%, transparent);
++ border: 1px solid color-mix(in srgb, var(--accent) 18%, transparent);
++ transition: color 0.2s, border-color 0.2s, background 0.2s;
++}
++#page-home .hero-latest:hover { color: var(--text-primary); border-color: color-mix(in srgb, var(--accent) 42%, transparent); background: color-mix(in srgb, var(--accent) 13%, transparent); }
++#page-home .hero-latest b { font-weight: 700; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
++#page-home .hero-latest i { color: var(--accent); flex: none; }
++@media (max-width: 480px) { #page-home .hero-latest { font-size: 0.7rem; padding: 5px 11px; max-width: 94vw; } }
ddiff --git a/public/index.html b/public/index.html
iindex ff1a4f86..d2ada5ca 100644
---- a/public/index.html
++++ b/public/index.html
@@@ -88,8 +88,8 @@
overlay) and is fetched without blocking the first paint: media="print" makes the browser
treat it as non-critical, and the onload flips it back to all once it has arrived. -->
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossorigin>
-- <link rel="stylesheet" href="/css/style.css?v=181">
-- <link rel="stylesheet" href="/css/home-fx.css?v=11">
++ <link rel="stylesheet" href="/css/style.css?v=183">
++ <link rel="stylesheet" href="/css/home-fx.css?v=12">
<link rel="stylesheet" href="/css/home-tour.css?v=7">
<link rel="stylesheet" href="/css/i18n-star.css?v=5">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
@@@ -307,6 +307,9 @@
<span aria-hidden="true">&bull;</span>
<a href="/dmca" class="hero-philosophy-link" onclick="return handleLinkClick(event, '/dmca')">DMCA</a>
</div>
++ <!-- Newest shipped commit — built in the open, receipts included. Sits under the
++ philosophy row so the proof is next to the promise. -->
++ <a class="hero-latest" id="hero-latest" href="/updates" onclick="return handleLinkClick(event, '/updates')" style="display:none"></a>
<div class="hero-stats" id="hero-stats" aria-label="Site stats"><div class="ovsk ovsk-panel" aria-hidden="true"><div class="ovsk-bar" style="height:14px;width:45%"></div><div class="ovsk-bar" style="height:10px;width:92%"></div><div class="ovsk-bar" style="height:10px;width:68%"></div></div></div>
</div>
</div>
@@@ -740,10 +743,7 @@
<div class="home-changelog-fade" id="home-changelog-fade"></div>
</div>
-- <!-- Newest shipped commit — built in the open, receipts included. It belongs under the
-- content, not in front of every stream on the site. -->
-- <!-- Newest shipped commit — built in the open, receipts included. -->
-- <a class="hero-latest" id="hero-latest" href="/updates" onclick="return handleLinkClick(event, '/updates')" style="display:none"></a>
++
</div>
@@@ -5130,7 +5130,7 @@ curl -s https://openvibe.media/live/@Goosely/chat-insight.json | jq ".chat_insig
<script src="/live-notify.js?v=3" defer></script>
<script src="/js/ov-skeleton.js?v=1" defer></script>
<script src="/js/ov-odometer.js?v=3" defer></script>
--<script src="/js/app.js?v=158" defer></script>
++<script src="/js/app.js?v=159" defer></script>
<script src="/js/stream-player.js?v=22" defer></script>
<script src="/js/stream-pip.js?v=1" defer></script>
<script src="/js/chat-shared.js?v=3" defer></script>
ddiff --git a/public/js/app.js b/public/js/app.js
iindex 07d7f2fc..f8456fbb 100644
---- a/public/js/app.js
++++ b/public/js/app.js
@@@ -1463,7 +1463,7 @@ function renderHeroStats(stats) {
metric: r.metric || null,
};
const clickable = !!r.metric;
-- return `<div class="hero-stat ${r.cls || ''} ${clickable ? 'hero-stat--clickable' : ''}" ${r.key ? `data-stat="${r.key}"` : ''} data-tip="${esc(JSON.stringify(tip))}" ${clickable ? `data-metric="${r.metric}" role="button" tabindex="0" aria-label="${esc(r.label)} — show over time"` : ''}><i class="fa-solid ${r.icon}"></i><div class="hero-stat-meta"><span class="hero-stat-numline"><span class="hero-stat-num" data-n="${r.num || 0}">0</span>${recDelta(r.recent, r.unit || '')}</span><span class="hero-stat-label">${r.label}${clickable ? ' <i class="fa-solid fa-chart-line hero-stat-chart-hint"></i>' : ''}</span>${sub && !r.recent ? `<span class="hero-stat-sub">${sub}</span>` : ''}</div></div>`;
++ return `<div class="hero-stat ${r.cls || ''} ${clickable ? 'hero-stat--clickable' : ''}" ${r.key ? `data-stat="${r.key}"` : ''} data-tip="${esc(JSON.stringify(tip))}" ${clickable ? `data-metric="${r.metric}" role="button" tabindex="0" aria-label="${esc(r.label)} — show over time"` : ''}><i class="fa-solid ${r.icon}"></i><div class="hero-stat-meta"><span class="hero-stat-num" data-n="${r.num || 0}">0</span><span class="hero-stat-label">${r.label}${clickable ? ' <i class="fa-solid fa-chart-line hero-stat-chart-hint"></i>' : ''}</span>${sub && !r.recent ? `<span class="hero-stat-sub">${sub}</span>` : ''}</div>${recDelta(r.recent, r.unit || '')}</div>`;
};
// The full board is four groups and two dozen chips. Shown by default it pushed every live
// stream on the site below the fold, and because it only arrives once the stats request lands