openvibers/openvibe.design Commits
Add WebSite + Organization JSON-LD to the home page (#109)
Changes
@@@ -13,6 +13,7 @@*/const layoutLib = require('../render/layout');const kit = require('openvibe-shared/kit');++const seo = require('openvibe-shared/seo');// The short home (the owner's rule, 2026-10-30): pick blocks and folds.const { pickBlock, pickRules, foldAround } = require('../render/short');const sc = require('openvibe-shared/showcase');@@@ -161,6 +162,7 @@ function home({ themes } = {}) {return layout({title: null, description: 'Your look, everywhere: one theme across every OpenVibe site, the same theme as code for the web, Android and iOS, and AI design on it, ranked honestly by the Design Arena.',path: '/', body: `<div class="ds-wrap">${body}</div>`, active: '/',++ jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Design', url: 'https://openvibe.design/', description: 'Your look, everywhere: one theme across every OpenVibe site, the same theme as code for the web, Android and iOS, and AI design on it, ranked honestly by the Design Arena.' }), seo.jsonLd.organization()],// The rule that shows the picked scene of the pick block: one per scene, written for this page.inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('scenes', arts.map((_, i) => `ds-scene-${i}`))}}}`],});@@@ -19,6 +19,10 @@ const { assetVersion } = require('../server/assets');assert.match(r.text, /href="\/tokens"/, 'the nav and the home lead to the tokens');assert.match(r.text, /<a class="ds-swatch" href="\/t\/aurora">[\s\S]*?<span class="ds-swatch-name">Aurora<\/span>/);assert.match(r.text, /<main id="main"/);++ const ld = [...r.text.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1])).flatMap((b) => b['@graph'] || [b]);++ assert.ok(ld.some((x) => x['@type'] === 'WebSite'), 'home has WebSite JSON-LD');++ const org = ld.find((x) => x['@type'] === 'Organization');++ assert.ok(org && Array.isArray(org.sameAs) && org.sameAs.includes('https://github.com/OpenVibers'), 'home has the OpenVibe Organization');assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=120');assert.match(r.headers.get('vary'), /Cookie/i);});
@@ -13,6 +13,7 @@
@@ -13,6 +13,7 @@
*/
*/
const layoutLib = require('../render/layout');
const layoutLib = require('../render/layout');
const kit = require('openvibe-shared/kit');
const kit = require('openvibe-shared/kit');
+const seo = require('openvibe-shared/seo');
// The short home (the owner's rule, 2026-10-30): pick blocks and folds.
// The short home (the owner's rule, 2026-10-30): pick blocks and folds.
const { pickBlock, pickRules, foldAround } = require('../render/short');
const { pickBlock, pickRules, foldAround } = require('../render/short');
const sc = require('openvibe-shared/showcase');
const sc = require('openvibe-shared/showcase');
@@ -161,6 +162,7 @@ function home({ themes } = {}) {
@@ -161,6 +162,7 @@ function home({ themes } = {}) {
return layout({
return layout({
title: null, description: 'Your look, everywhere: one theme across every OpenVibe site, the same theme as code for the web, Android and iOS, and AI design on it, ranked honestly by the Design Arena.',
title: null, description: 'Your look, everywhere: one theme across every OpenVibe site, the same theme as code for the web, Android and iOS, and AI design on it, ranked honestly by the Design Arena.',
path: '/', body: `<div class="ds-wrap">${body}</div>`, active: '/',
path: '/', body: `<div class="ds-wrap">${body}</div>`, active: '/',
+ jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Design', url: 'https://openvibe.design/', description: 'Your look, everywhere: one theme across every OpenVibe site, the same theme as code for the web, Android and iOS, and AI design on it, ranked honestly by the Design Arena.' }), seo.jsonLd.organization()],
// The rule that shows the picked scene of the pick block: one per scene, written for this page.
// The rule that shows the picked scene of the pick block: one per scene, written for this page.
inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('scenes', arts.map((_, i) => `ds-scene-${i}`))}}}`],
inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('scenes', arts.map((_, i) => `ds-scene-${i}`))}}}`],
});
});
@@ -19,6 +19,10 @@ const { assetVersion } = require('../server/assets');
@@ -19,6 +19,10 @@ const { assetVersion } = require('../server/assets');
assert.match(r.text, /href="\/tokens"/, 'the nav and the home lead to the tokens');
assert.match(r.text, /href="\/tokens"/, 'the nav and the home lead to the tokens');
assert.match(r.text, /<a class="ds-swatch" href="\/t\/aurora">[\s\S]*?<span class="ds-swatch-name">Aurora<\/span>/);
assert.match(r.text, /<a class="ds-swatch" href="\/t\/aurora">[\s\S]*?<span class="ds-swatch-name">Aurora<\/span>/);
assert.match(r.text, /<main id="main"/);
assert.match(r.text, /<main id="main"/);
+ const ld = [...r.text.matchAll(/<script type="application\/ld\+json">([\s\S]*?)<\/script>/g)].map((m) => JSON.parse(m[1])).flatMap((b) => b['@graph'] || [b]);
+ assert.ok(ld.some((x) => x['@type'] === 'WebSite'), 'home has WebSite JSON-LD');
+ const org = ld.find((x) => x['@type'] === 'Organization');
+ assert.ok(org && Array.isArray(org.sameAs) && org.sameAs.includes('https://github.com/OpenVibers'), 'home has the OpenVibe Organization');
assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=120');
assert.strictEqual(r.headers.get('cache-control'), 'public, max-age=120');
assert.match(r.headers.get('vary'), /Cookie/i);
assert.match(r.headers.get('vary'), /Cookie/i);
});
});