openvibers/openvibe.games Commits
Games' front page on a phone: the games are a row you swipe, the featured one first (13.1 screens -> about 6) (#206)
Changes
@@@ -779,6 +779,24 @@#navbar-mount:not(:empty) + .ov-nav-fallback {display: none;}++ /* Phones (2026-10-29): a taste and a door, not every card stacked. The numbers, the games and the achievements are++ rows you swipe, the featured game first; a card says four lines; the steps are a compact list. */++ @media (max-width: 599px) {++ .home-stats { display: flex; overflow-x: auto; scrollbar-width: none; margin: 22px -16px 0; padding: 0 16px; gap: 8px; }++ .home-stats::-webkit-scrollbar { display: none; }++ .home-stats li { flex: 0 0 auto; min-width: 118px; padding: 10px 12px; }++ .home-stats b { font-size: 22px; }++ .games { display: grid; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 86%; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 16px; scrollbar-width: none; margin: 18px 0 36px; padding: 0 16px 8px; gap: 12px; }++ .games::-webkit-scrollbar { display: none; }++ .games > .game-card, .games > .game-card.featured, .games > .game-card.wide { grid-column: auto; grid-template-columns: 1fr; scroll-snap-align: start; }++ .game-card:hover { transform: none; }++ .game-body > p { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }++ .ach-grid { display: grid; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 72%; overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; gap: 10px; padding-bottom: 6px; }++ .ach-grid::-webkit-scrollbar { display: none; }++ .ach-grid > * { scroll-snap-align: start; }++ .how-steps { gap: 0; }++ .how-steps li { padding: 10px 0; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; background: none; }++ }</style></head><body>@@@ -1214,18 +1232,6 @@<!--ov:home-sections-->-- <section class="account">-- <div class="account-inner">-- <h2>Play as a guest. <b>Keep everything</b> with an OpenVibe account.</h2>-- <p>-- Jump into any game instantly as a guest: your characters live in this browser. Sign in-- with your <b>OpenVibe</b> account to keep them and your progress on every device, and to-- carry one identity across openvibe.live, openvibe.tools and every game here.-- </p>-- <a class="signin" href="/auth/login?next=%2F">Sign in with OpenVibe</a>-- <div class="fine">One account. All of OpenVibe.</div>-- </div>-- </section><!-- What shipped on OpenVibe.Games: the network's shared "shipped" views (openvibe-sharedshipped.js, which the shared footer loads), the same on every OpenVibe site. -->@@@ -10,6 +10,7 @@ import {createPortalHome,readPlayTitles,renderHome,++ steamSection,renderStats,yourStuffSection,type HomeData,@@@ -125,10 +126,13 @@ describe('the home page live sections', () => {expect(parts.sections).toContain('Most visited')expect(parts.sections).toContain('40 visits')expect(parts.stats).toContain('<b>7</b><span>creator worlds published</span>')-- expect(parts.sections).toContain('https://store.steampowered.com/app/2/')-- expect(parts.sections).toContain('−50%')-- expect(parts.sections).toContain('checked 2 hours ago')-- expect(parts.sections).not.toContain('home-steam-new')++ // The Steam lists left the home (2026-10-29: the "On Steam right now" card links them); they render on their own.++ expect(parts.sections).not.toContain('store.steampowered.com')++ const steam = steamSection(d, NOW)++ expect(steam).toContain('https://store.steampowered.com/app/2/')++ expect(steam).toContain('−50%')++ expect(steam).toContain('checked 2 hours ago')++ expect(steam).not.toContain('home-steam-new')})it('a stat with nothing to say is left out', () => {@@@ -181,7 +185,8 @@ describe('the home page live sections', () => {const home = createPortalHome(src, { now: () => now, log: { warn: (m) => warnings.push(m) } })const first = await home()expect(first.stats).toContain('player online now')-- expect(first.sections).toContain('id="home-steam"')++ expect(first.sections).toContain('id="home-how"')++ expect(first.sections).not.toContain('id="home-steam"')expect(first.sections).not.toContain('home-achievements')now += 30_000await home()@@@ -319,7 +319,8 @@ function steamRow(g: SteamGame): string {}<span class="steam-text"><b>${esc(g.name)}</b>${price ? `<span class="steam-price">${price}</span>` : ''}</span></a></li>`}--function steamSection(d: HomeData, now: number): string {++/** The Steam lists (off the home since 2026-10-29: the "On Steam right now" card links them). */++export function steamSection(d: HomeData, now: number): string {const s = d.steamif (!s) return ''const deals = [...s.sale].sort((a, b) => b.discount_percent - a.discount_percent)@@@ -468,7 +469,6 @@ export function renderHome(boardsSection(d),achievementsSection(d),worldsSection(d, now),-- steamSection(d, now),howSection(),faqSection(),]
@@ -779,6 +779,24 @@
@@ -779,6 +779,24 @@
#navbar-mount:not(:empty) + .ov-nav-fallback {
#navbar-mount:not(:empty) + .ov-nav-fallback {
display: none;
display: none;
}
}
+ /* Phones (2026-10-29): a taste and a door, not every card stacked. The numbers, the games and the achievements are
+ rows you swipe, the featured game first; a card says four lines; the steps are a compact list. */
+ @media (max-width: 599px) {
+ .home-stats { display: flex; overflow-x: auto; scrollbar-width: none; margin: 22px -16px 0; padding: 0 16px; gap: 8px; }
+ .home-stats::-webkit-scrollbar { display: none; }
+ .home-stats li { flex: 0 0 auto; min-width: 118px; padding: 10px 12px; }
+ .home-stats b { font-size: 22px; }
+ .games { display: grid; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 86%; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: 16px; scrollbar-width: none; margin: 18px 0 36px; padding: 0 16px 8px; gap: 12px; }
+ .games::-webkit-scrollbar { display: none; }
+ .games > .game-card, .games > .game-card.featured, .games > .game-card.wide { grid-column: auto; grid-template-columns: 1fr; scroll-snap-align: start; }
+ .game-card:hover { transform: none; }
+ .game-body > p { display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden; }
+ .ach-grid { display: grid; grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 72%; overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; gap: 10px; padding-bottom: 6px; }
+ .ach-grid::-webkit-scrollbar { display: none; }
+ .ach-grid > * { scroll-snap-align: start; }
+ .how-steps { gap: 0; }
+ .how-steps li { padding: 10px 0; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; background: none; }
+ }
</style>
</style>
</head>
</head>
<body>
<body>
@@ -1214,18 +1232,6 @@
@@ -1214,18 +1232,6 @@
<!--ov:home-sections-->
<!--ov:home-sections-->
- <section class="account">
- <div class="account-inner">
- <h2>Play as a guest. <b>Keep everything</b> with an OpenVibe account.</h2>
- <p>
- Jump into any game instantly as a guest: your characters live in this browser. Sign in
- with your <b>OpenVibe</b> account to keep them and your progress on every device, and to
- carry one identity across openvibe.live, openvibe.tools and every game here.
- </p>
- <a class="signin" href="/auth/login?next=%2F">Sign in with OpenVibe</a>
- <div class="fine">One account. All of OpenVibe.</div>
- </div>
- </section>
<!-- What shipped on OpenVibe.Games: the network's shared "shipped" views (openvibe-shared
<!-- What shipped on OpenVibe.Games: the network's shared "shipped" views (openvibe-shared
shipped.js, which the shared footer loads), the same on every OpenVibe site. -->
shipped.js, which the shared footer loads), the same on every OpenVibe site. -->
@@ -10,6 +10,7 @@ import {
@@ -10,6 +10,7 @@ import {
createPortalHome,
createPortalHome,
readPlayTitles,
readPlayTitles,
renderHome,
renderHome,
+ steamSection,
renderStats,
renderStats,
yourStuffSection,
yourStuffSection,
type HomeData,
type HomeData,
@@ -125,10 +126,13 @@ describe('the home page live sections', () => {
@@ -125,10 +126,13 @@ describe('the home page live sections', () => {
expect(parts.sections).toContain('Most visited')
expect(parts.sections).toContain('Most visited')
expect(parts.sections).toContain('40 visits')
expect(parts.sections).toContain('40 visits')
expect(parts.stats).toContain('<b>7</b><span>creator worlds published</span>')
expect(parts.stats).toContain('<b>7</b><span>creator worlds published</span>')
- expect(parts.sections).toContain('https://store.steampowered.com/app/2/')
- expect(parts.sections).toContain('−50%')
- expect(parts.sections).toContain('checked 2 hours ago')
- expect(parts.sections).not.toContain('home-steam-new')
+ // The Steam lists left the home (2026-10-29: the "On Steam right now" card links them); they render on their own.
+ expect(parts.sections).not.toContain('store.steampowered.com')
+ const steam = steamSection(d, NOW)
+ expect(steam).toContain('https://store.steampowered.com/app/2/')
+ expect(steam).toContain('−50%')
+ expect(steam).toContain('checked 2 hours ago')
+ expect(steam).not.toContain('home-steam-new')
})
})
it('a stat with nothing to say is left out', () => {
it('a stat with nothing to say is left out', () => {
@@ -181,7 +185,8 @@ describe('the home page live sections', () => {
@@ -181,7 +185,8 @@ describe('the home page live sections', () => {
const home = createPortalHome(src, { now: () => now, log: { warn: (m) => warnings.push(m) } })
const home = createPortalHome(src, { now: () => now, log: { warn: (m) => warnings.push(m) } })
const first = await home()
const first = await home()
expect(first.stats).toContain('player online now')
expect(first.stats).toContain('player online now')
- expect(first.sections).toContain('id="home-steam"')
+ expect(first.sections).toContain('id="home-how"')
+ expect(first.sections).not.toContain('id="home-steam"')
expect(first.sections).not.toContain('home-achievements')
expect(first.sections).not.toContain('home-achievements')
now += 30_000
now += 30_000
await home()
await home()
@@ -319,7 +319,8 @@ function steamRow(g: SteamGame): string {
@@ -319,7 +319,8 @@ function steamRow(g: SteamGame): string {
}<span class="steam-text"><b>${esc(g.name)}</b>${price ? `<span class="steam-price">${price}</span>` : ''}</span></a></li>`
}<span class="steam-text"><b>${esc(g.name)}</b>${price ? `<span class="steam-price">${price}</span>` : ''}</span></a></li>`
}
}
-function steamSection(d: HomeData, now: number): string {
+/** The Steam lists (off the home since 2026-10-29: the "On Steam right now" card links them). */
+export function steamSection(d: HomeData, now: number): string {
const s = d.steam
const s = d.steam
if (!s) return ''
if (!s) return ''
const deals = [...s.sale].sort((a, b) => b.discount_percent - a.discount_percent)
const deals = [...s.sale].sort((a, b) => b.discount_percent - a.discount_percent)
@@ -468,7 +469,6 @@ export function renderHome(
@@ -468,7 +469,6 @@ export function renderHome(
boardsSection(d),
boardsSection(d),
achievementsSection(d),
achievementsSection(d),
worldsSection(d, now),
worldsSection(d, now),
- steamSection(d, now),
howSection(),
howSection(),
faqSection(),
faqSection(),
]
]