openvibers/openvibe.space Commits

Space: the home page is built from the network's showcase sections, in the Frame's font

OP OpenVibers committed 10 Oct 2026 · 6916fe7f16 · 4 files, +89 −47

Space's home rendered in the browser's default serif and said little: one paragraph and a box, with the footer floating mid-page. It is now built from the network's showcase sections (`openvibe-shared/showcase`: hero, features, steps), so it reads like every other product home (News, Quest, Watch, Bot), in the Frame's font.

The copy stays honest. The hero says Space is being rebuilt and that nothing is hosted yet. "What Space will host" lists the parts from plan T20 step 2 (static sites, dynamic pages on Run workers, spaces with account grants, templates and forks, building with an agent, names under openvibe.website), each as something Space *will* host. "Where things are today" links what exists now: the forum on OpenVibe.Community, the developer platform on openvibe.services and the source code.

Error pages keep their own small styles in the same type. Tests updated (`test/home.test.js`, `test/boot.test.js`). Screenshots checked at 1280 and 390 px with no overflow.

Changes

ddiff --git a/public/css/space.css b/public/css/space.css
iindex ac177fb..9d55bf2 100644
---- a/public/css/space.css
++++ b/public/css/space.css
@@@ -1,51 +1,66 @@
/* OpenVibe.Space — the home page and error pages. The shared Frame (openvibe-shared/shell) brings the
-- theme variables, navbar and footer; this is only what this site renders itself. */
++ theme variables, navbar and footer, and the home page is built from openvibe-shared/showcase sections
++ (showcase.css); this is only what this site renders itself. */
++body {
++ font-family: var(--ov-font, 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif);
++}
++
++/* The showcase sections carry their own width and gutters; the page only spaces them from the footer. */
.page {
-- max-width: 60rem;
++ padding: 0 0 72px;
++}
++
++/* Error pages: one centred column in the same type as the showcase hero. */
++.error {
++ max-width: 1080px;
margin: 0 auto;
-- padding: 4rem 1.25rem 6rem;
++ padding: 56px 24px 0;
}
--.hero h1 {
-- font-size: clamp(1.75rem, 4vw, 2.75rem);
-- line-height: 1.15;
-- margin: 0.25rem 0 1rem;
++.error h1 {
++ font-size: clamp(28px, 4vw, 44px);
++ line-height: 1.1;
++ letter-spacing: -0.02em;
++ margin: 0.25rem 0 0.75rem;
}
.eyebrow {
-- text-transform: uppercase;
-- letter-spacing: 0.08em;
-- font-size: 0.75rem;
-- font-weight: 600;
-- opacity: 0.7;
++ display: inline-block;
margin: 0;
++ padding: 4px 12px;
++ border-radius: 999px;
++ border: 1px solid var(--border-light, var(--border, #2c3d5c));
++ background: var(--bg-secondary, #101828);
++ color: var(--accent-light, var(--accent, #60a5fa));
++ font-size: 12px;
++ font-weight: 700;
++ letter-spacing: 0.4px;
}
.lede {
-- font-size: 1.125rem;
++ font-size: 17px;
line-height: 1.6;
-- max-width: 44rem;
--}
--
--.forum-note {
-- margin-top: 1.5rem;
-- padding: 0.875rem 1rem;
-- border: 1px solid currentColor;
-- border-radius: 0.5rem;
-- font-weight: 600;
--}
--
--.muted {
-- opacity: 0.7;
-- font-size: 0.9375rem;
++ max-width: 640px;
++ color: var(--text-secondary, #96a7c2);
}
.btn {
-- display: inline-block;
-- margin-right: 0.5rem;
-- padding: 0.5rem 0.875rem;
-- border: 1px solid currentColor;
-- border-radius: 0.375rem;
++ display: inline-flex;
++ align-items: center;
++ margin: 8px 8px 0 0;
++ padding: 10px 18px;
++ border-radius: 12px;
++ border: 1px solid var(--border-light, var(--border, #2c3d5c));
++ color: var(--text-primary, #e6edf7);
++ font-weight: 700;
++ font-size: 15px;
text-decoration: none;
}
++
++.btn:hover,
++.btn:focus-visible {
++ border-color: var(--accent, #3b82f6);
++ background: var(--bg-hover, #1c2a44);
++ outline: 0;
++}
ddiff --git a/server/render/pages.js b/server/render/pages.js
iindex 1385afe..f172965 100644
---- a/server/render/pages.js
++++ b/server/render/pages.js
@@@ -5,25 +5,50 @@
* page for crawlers and no-JS readers; the shared Frame adds the navbar, footer and theme.
*
* Space publishes one page of its own while it is rebuilt: the home page, which says what Space will
-- * host and points at the forum on OpenVibe.Community. It claims nothing Space does not have.
++ * host and points at the forum on OpenVibe.Community and the developer platform. It claims nothing Space does not have.
*/
const config = require('../config');
++const sc = require('openvibe-shared/showcase');
const { renderPage, escapeHtml: esc } = require('./layout');
const FORUM_URL = `${config.communityUrl}/s`;
const FORUM_LABEL = FORUM_URL.replace(/^https?:\/\//, '');
++const SERVICES_URL = 'https://openvibe.services/';
--/** The home page: what Space will host, and where the forum went. */
++/** The home page: what Space will host, and where things are today. Built from the network's showcase sections
++ * (openvibe-shared/showcase), so it reads like every other product home. It promises nothing as open: every card says
++ * what Space WILL host, and the live links go to the forum and the developer platform. */
function homePage() {
-- const body = `
--<section class="hero">
-- <p class="eyebrow">OpenVibe.Space</p>
-- <h1>Code and dynamic pages for the OpenVibe network</h1>
-- <p class="lede">Space is being rebuilt to host <strong>code</strong> and <strong>dynamic pages</strong>, and <strong>spaces</strong>: apps people build that use their OpenVibe account, the network's services and the SDK.</p>
-- <p class="forum-note">The forum moved to OpenVibe.Community → <a href="${esc(FORUM_URL)}">${esc(FORUM_LABEL)}</a></p>
-- <p class="muted">Nothing is published here yet.</p>
--</section>`;
-- return renderPage({ canonicalPath: '/', active: 'home', body });
++ const body = sc.hero({
++ eyebrow: 'OpenVibe.Space · being rebuilt',
++ title: 'Code and dynamic pages,',
++ accent: 'on your OpenVibe account.',
++ lede: 'OpenVibe.Space is being rebuilt to host static sites, dynamic pages and spaces: small apps people make that sign in with OpenVibe and use the network\'s services through the SDK. Nothing is hosted here yet.',
++ actions: [
++ { label: 'The forum is on OpenVibe.Community', href: FORUM_URL, primary: true, icon: 'fa-comments' },
++ { label: 'Build on OpenVibe today', href: SERVICES_URL, icon: 'fa-code' },
++ ],
++ note: `The forum moved to OpenVibe.Community (${FORUM_LABEL}); every old forum address redirects there.`,
++ }) + sc.features({
++ title: 'What Space will host',
++ lede: 'Each part is composed from services the network already runs. None of it is open yet.',
++ items: [
++ { icon: 'fa-file-code', title: 'Static sites', text: 'Upload a folder or connect a Git repository. Every deploy is kept, and the active one rolls back in one step.' },
++ { icon: 'fa-bolt', title: 'Dynamic pages', text: 'Server functions that run on OpenVibe.Run workers, with their files kept on OpenVibe.Media.' },
++ { icon: 'fa-cubes', title: 'Spaces', text: 'Small apps that sign people in with their OpenVibe account and ask only for the grants they need.' },
++ { icon: 'fa-code-branch', title: 'Templates and forks', text: 'Start from a template, or fork someone\'s space and make it yours.' },
++ { icon: 'fa-wand-magic-sparkles', title: 'Build with an agent', text: 'Describe a space and an agent on OpenVibe.Actor and OpenVibe.Codes builds it, with every change shown.' },
++ { icon: 'fa-globe', title: 'Your own address', text: 'Each site gets a name under openvibe.website; a custom domain is verified before it is served.' },
++ ],
++ }) + sc.steps({
++ title: 'Where things are today',
++ items: [
++ { title: 'The forum', href: FORUM_URL, text: 'Boards, threads, votes and moderation moved to OpenVibe.Community.' },
++ { title: 'The developer platform', href: SERVICES_URL, text: 'Projects, credentials, grants and the SDK that spaces will be built on.' },
++ { title: 'The source code', href: 'https://github.com/OpenVibers/OpenVibe.Space', text: 'Space is open source (AGPL-3.0): follow the rebuild there.' },
++ ],
++ });
++ return renderPage({ canonicalPath: '/', active: 'home', styles: [sc.STYLESHEET], body });
}
// ── Errors ───────────────────────────────────────────────────
ddiff --git a/test/boot.test.js b/test/boot.test.js
iindex b98d8d4..fe5fa5c 100644
---- a/test/boot.test.js
++++ b/test/boot.test.js
@@@ -68,7 +68,7 @@ async function check(name, fn) {
assert.strictEqual(r.status, 200);
const html = await r.text();
assert.match(html, /<title>OpenVibe\.Space — code and dynamic pages<\/title>/);
-- assert.match(html, /<h1>Code and dynamic pages for the OpenVibe network<\/h1>/);
++ assert.match(html, /<h1>Code and dynamic pages,<span class="sc-accent"> on your OpenVibe account\.<\/span><\/h1>/);
assert.match(html, /The forum moved to OpenVibe\.Community/);
assert.match(html, /href="https:\/\/openvibe\.community\/s"/);
});
ddiff --git a/test/home.test.js b/test/home.test.js
iindex 972cf12..f937d29 100644
---- a/test/home.test.js
++++ b/test/home.test.js
@@@ -13,9 +13,11 @@ const { boot, check, done } = require('./helpers/app');
await check('the home page says what Space will host and links the forum on OpenVibe.Community', async () => {
const r = await t.get('/');
assert.strictEqual(r.status, 200);
-- assert.match(r.text, /<h1>Code and dynamic pages for the OpenVibe network<\/h1>/);
-- assert.match(r.text, /<strong>code<\/strong> and <strong>dynamic pages<\/strong>/);
-- assert.match(r.text, /<strong>spaces<\/strong>/);
++ assert.match(r.text, /<h1>Code and dynamic pages,<span class="sc-accent"> on your OpenVibe account\.<\/span><\/h1>/);
++ assert.match(r.text, /static sites, dynamic pages and spaces/);
++ assert.match(r.text, /Nothing is hosted here yet\./, 'it claims nothing is open');
++ assert.match(r.text, /What Space will host/);
++ assert.match(r.text, /showcase\.css/, 'built from the network\'s showcase sections');
assert.match(r.text, /The forum moved to OpenVibe\.Community/);
assert.match(r.text, /href="https:\/\/openvibe\.community\/s"/, 'the forum link points at Community');
assert.match(r.text, /<link rel="canonical" href="https:\/\/openvibe\.space\/">/);