openvibers/openvibe.surf Commits
Every scene shows on a phone; the chips jump to them (#67)
Changes
@@@ -262,29 +262,22 @@ html[data-ov-motion=reduced] .sf-desk-page[data-sf-desk-open="1"] .sf-desk-state/* The phone home (2026-10-30, the owner's short-pages rule): the three doors are one sideways row, the scenes arechips, one at a time, and the questions are a fold. Everything stays in the page for the crawlers. */--/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page's blocks of the same kind are one at a-- time, picked with a row of chips. Radio inputs and :has(), no script: the chips and the one-pane rule live under-- 640 px and only where :has() is understood, so a wider screen, or an older browser, shows every pane and no chips.-- Every pane stays in the page for crawlers. */--.pick-head { display: none; }--.pick-row { display: none; }--@supports selector(:has(*)) { @media (max-width: 640px) {++/* Pick: on a phone every pane is shown, stacked, each with its own heading; the chips are a row of links that jump to++ a pane (the owner's rule, 2026-10-31: never hide content to shorten). A wide screen shows the panes as before, with++ no chips. */++.pick-head, .pick-row { display: none; }++@media (max-width: 640px) {.pick-bar { margin-top: 30px; }.pick-head { display: block; }.pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px, 2.4vw, 28px)/1.2 var(--ov-font, 'Inter', system-ui, sans-serif); letter-spacing: -.02em; }.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }.pick-row::-webkit-scrollbar { display: none; }-- .pick-row input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }-- .pick-row label { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; cursor: pointer; }-- .pick-row input:checked + label { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); background: color-mix(in srgb, var(--accent, #3b82f6) 12%, transparent); }-- .pick-row input:focus-visible + label { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }-- .pick .pick-pane { display: none; }-- /* A link that points at a pane opens that pane on a phone too. */-- .pick .pick-pane:target { display: block !important; }-- .pick:has(.pick-pane:target) .pick-pane:not(:target) { display: none !important; }-- /* The chip names the pane, so the pane's own heading gives way to it; the link to more stays. */-- .pick .pick-pane > h2, .pick .pick-pane > h3 { display: none; }--} }++ .pick-chip { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; text-decoration: none; }++ .pick-chip:hover, .pick-chip:focus-visible { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); }++ .pick-chip:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }++ .pick .pick-pane { scroll-margin-top: 72px; }++ .pick .pick-pane + .pick-pane { margin-top: 26px; }++}/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section itreplaces: a heading and its content, with no card around it. */@@@ -86,13 +86,11 @@ function home() {});const at = room.indexOf('<section class="sc-room-scenes"');let body = at < 0 ? room.replace(/<\/div>$/, `${tiles}</div>`) : room.slice(0, at) + tiles + room.slice(at);-- // The home on a phone (the owner's short-pages rule, 2026-10-30): the three scenes are chips, one at a time, and-- // the questions are a fold. Both stay in the page for the crawlers.++ // The home on a phone (the owner, 2026-10-31: never hide content to shorten a page): the three scenes are all shown,++ // the chips jump to them, and the questions are a counted fold.body = S.pickScenes(body, { key: 'scenes', label: 'What you can do', head: 'What you can do', labels: ['Ride maps', 'Surf the web', 'Watch'] });body = S.foldSection(body, 'id="sc-questions"', 'questions-fold');-- // The rule that shows the picked scene: one per scene, so the CSS is written for this page.-- const rules = `@supports selector(:has(*)){@media (max-width:640px){${S.pickRules('scenes', ['scene-0', 'scene-1', 'scene-2'])}}}`;-- return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/', jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Surf', url: 'https://openvibe.surf/', description: 'Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.' }), seo.jsonLd.organization()], inlineStyles: [rules] });++ return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/', jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Surf', url: 'https://openvibe.surf/', description: 'Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.' }), seo.jsonLd.organization()] });}function mapCard(m) {@@@ -25,15 +25,15 @@ function pickBlock({ key, label, head = '', panes, bodyClass = '' }) {// A pane with nothing to show is left out, so the chips and the rules that show them match the page.panes = (panes || []).filter((p) => p.html);if (!panes.length) return '';-- const chips = panes.map((p, i) => `<input type="radio" name="pick-${esc(key)}" id="pk-${esc(key)}-${esc(p.id)}" value="${esc(p.id)}"${i === 0 ? ' checked' : ''}><label for="pk-${esc(key)}-${esc(p.id)}">${esc(p.label)}</label>`).join('');-- // The pane carries the id the page already links to (an anchor in the hero, for instance), so a link to it still-- // opens that pane on a phone (the CSS shows the pane a link points at).++ // Every pane is shown (the owner, 2026-10-31: never hide content to shorten a page); the chips are links that jump++ // to a pane. They used to be radio chips that showed one pane and hid the others.++ const chips = panes.map((p) => `<a class="pick-chip" href="#${esc(p.id)}">${esc(p.label)}</a>`).join('');const body = panes.map((p) => `<div class="pick-pane" id="${esc(p.id)}">${p.html}</div>`).join('');-- const bar = `${head ? `<div class="pick-head"><h2>${esc(head)}</h2></div>` : ''}<div class="pick-row" role="radiogroup" aria-label="${esc(label)}">${chips}</div>`;++ const bar = `${head ? `<div class="pick-head"><h2>${esc(head)}</h2></div>` : ''}<nav class="pick-row" aria-label="${esc(label)}">${chips}</nav>`;return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar">${bar}</div><div class="pick-body${bodyClass ? ' ' + esc(bodyClass) : ''}">${body}</div></div>`;}--/** The rule that shows the picked block of a pick block: one per block, so the CSS is written for the page. */++/** No longer needed (every pane shows): kept so a caller that still asks for the rules gets none. */function pickRule(key, id) {return `.pick:has(#pk-${key}-${id}:checked) #${id}{display:block}`;}@@@ -1,8 +1,8 @@'use strict';/**-- * The home on a phone (the owner's short-pages rule, 2026-10-30): the three scenes are a pick block, a row of chips-- * picks one of them, and the questions are a fold. Radio inputs and :has(), no script for the chips; every scene stays-- * in the page for crawlers; a wider screen shows them all and no chips.++ * The home on a phone (the owner, 2026-10-31: never hide content to shorten a page): the three scenes are all shown,++ * a row of chips links to each, and the questions are a counted fold. The chips used to pick one scene and hide the++ * others.*/const assert = require('assert');const fs = require('fs');@@@ -12,31 +12,28 @@ const { boot, check, done } = require('./helpers/boot');(async () => {const t = await boot();try {-- await check('the scenes are chips: one per scene, the first picked, every scene in the page', async () => {++ await check('every scene is shown; the chips are links that jump to one (the owner, 2026-10-31)', async () => {const r = await t.get('/');assert.strictEqual(r.status, 200, r.text);const page = r.text;-- assert.ok(page.includes('<div class="pick" id="pick-scenes">'), 'the pick block is on the home');++ assert.ok(page.includes('<div class="pick" id="pick-scenes">'), 'the scenes block is on the home');const block = page.slice(page.indexOf('<div class="pick" id="pick-scenes">'));-- const chips = [...block.matchAll(/<input type="radio" name="pick-scenes" id="pk-scenes-(scene-\d+)" value="\1"( checked)?><label for="pk-scenes-\1">([^<]+)<\/label>/g)];-- assert.deepStrictEqual(chips.map((m) => m[1]), ['scene-0', 'scene-1', 'scene-2'], 'a chip per scene');-- assert.deepStrictEqual(chips.filter((m) => m[2]).map((m) => m[1]), ['scene-0'], 'the first scene is picked');++ assert.ok(!/type="radio"/.test(block), 'no radio chips');++ const chips = [...block.matchAll(/<a class="pick-chip" href="#(scene-\d+)">([^<]+)<\/a>/g)];++ assert.deepStrictEqual(chips.map((m) => m[1]), ['scene-0', 'scene-1', 'scene-2'], 'a link per scene');const panes = [...block.matchAll(/<div class="pick-pane" id="scene-(\d+)">/g)].map((m) => m[1]);-- assert.deepStrictEqual(panes, ['0', '1', '2'], 'every scene is in the page, one per chip');-- // Nothing was removed: the scenes and the doors are still there.++ assert.deepStrictEqual(panes, ['0', '1', '2'], 'every scene is in the page');for (const title of ['Press play, you are on the ramp', 'Surf the web with Vibe', 'Lean back and surf what is on', 'Ride', 'Web', 'Watch']) {assert.ok(page.includes(title), `${title} is on the page`);}});-- await check('the CSS guard: the chips are hidden and every scene shows until 640 px and where :has() is understood', async () => {++ await check('the CSS: on a phone nothing is hidden; the chips are a row of 44 px links', async () => {const r = await t.get('/');-- assert.match(r.text, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-scenes-scene-0:checked\) #scene-0\{display:block\}/, 'the rule that shows the picked scene');++ assert.ok(!/\.pick:has\(#pk-scenes/.test(r.text), 'no per-scene rule in the page');const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'surf.css'), 'utf8');-- assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');-- assert.match(css, /@supports selector\(:has\(\*\)\) \{ @media \(max-width: 640px\) \{/, 'the phone rules are under :has() and 640 px');-- assert.match(css, /\.pick-row label \{[^}]*min-height: 44px/, 'a chip is a 44 px target');-- assert.match(css, /\.pick \.pick-pane \{ display: none; \}/, 'the scenes are hidden on a phone');++ assert.ok(!/pick-pane \{ display: none/.test(css), 'no scene is hidden');++ assert.match(css, /\.pick-chip \{[^}]*min-height: 44px/, 'a chip is a 44 px target');assert.match(css, /\.sf-tiles \{ display: flex; overflow-x: auto;/, 'the three doors are one sideways row');});
@@ -262,29 +262,22 @@ html[data-ov-motion=reduced] .sf-desk-page[data-sf-desk-open="1"] .sf-desk-state
@@ -262,29 +262,22 @@ html[data-ov-motion=reduced] .sf-desk-page[data-sf-desk-open="1"] .sf-desk-state
/* The phone home (2026-10-30, the owner's short-pages rule): the three doors are one sideways row, the scenes are
/* The phone home (2026-10-30, the owner's short-pages rule): the three doors are one sideways row, the scenes are
chips, one at a time, and the questions are a fold. Everything stays in the page for the crawlers. */
chips, one at a time, and the questions are a fold. Everything stays in the page for the crawlers. */
-/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page's blocks of the same kind are one at a
- time, picked with a row of chips. Radio inputs and :has(), no script: the chips and the one-pane rule live under
- 640 px and only where :has() is understood, so a wider screen, or an older browser, shows every pane and no chips.
- Every pane stays in the page for crawlers. */
-.pick-head { display: none; }
-.pick-row { display: none; }
-@supports selector(:has(*)) { @media (max-width: 640px) {
+/* Pick: on a phone every pane is shown, stacked, each with its own heading; the chips are a row of links that jump to
+ a pane (the owner's rule, 2026-10-31: never hide content to shorten). A wide screen shows the panes as before, with
+ no chips. */
+.pick-head, .pick-row { display: none; }
+@media (max-width: 640px) {
.pick-bar { margin-top: 30px; }
.pick-bar { margin-top: 30px; }
.pick-head { display: block; }
.pick-head { display: block; }
.pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px, 2.4vw, 28px)/1.2 var(--ov-font, 'Inter', system-ui, sans-serif); letter-spacing: -.02em; }
.pick-head h2 { margin: 0 0 4px; font: 700 clamp(20px, 2.4vw, 28px)/1.2 var(--ov-font, 'Inter', system-ui, sans-serif); letter-spacing: -.02em; }
.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }
.pick-row { display: flex; gap: 8px; overflow-x: auto; margin: 10px -16px 0; padding: 2px 16px 4px; scrollbar-width: none; overscroll-behavior-x: contain; }
.pick-row::-webkit-scrollbar { display: none; }
.pick-row::-webkit-scrollbar { display: none; }
- .pick-row input { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
- .pick-row label { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; cursor: pointer; }
- .pick-row input:checked + label { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); background: color-mix(in srgb, var(--accent, #3b82f6) 12%, transparent); }
- .pick-row input:focus-visible + label { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }
- .pick .pick-pane { display: none; }
- /* A link that points at a pane opens that pane on a phone too. */
- .pick .pick-pane:target { display: block !important; }
- .pick:has(.pick-pane:target) .pick-pane:not(:target) { display: none !important; }
- /* The chip names the pane, so the pane's own heading gives way to it; the link to more stays. */
- .pick .pick-pane > h2, .pick .pick-pane > h3 { display: none; }
-} }
+ .pick-chip { display: inline-flex; align-items: center; flex: none; min-height: 44px; padding: 0 15px; border-radius: 999px; border: 1px solid var(--border, #1f2d47); color: var(--text-secondary, #96a7c2); font-size: .9rem; font-weight: 650; white-space: nowrap; text-decoration: none; }
+ .pick-chip:hover, .pick-chip:focus-visible { color: var(--text-primary, #e6edf7); border-color: var(--accent, #3b82f6); }
+ .pick-chip:focus-visible { outline: 2px solid var(--accent, #3b82f6); outline-offset: 2px; }
+ .pick .pick-pane { scroll-margin-top: 72px; }
+ .pick .pick-pane + .pick-pane { margin-top: 26px; }
+}
/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section it
/* The fold (the owner's short-pages rule): closed on a phone, and where there is room it looks like the section it
replaces: a heading and its content, with no card around it. */
replaces: a heading and its content, with no card around it. */
@@ -86,13 +86,11 @@ function home() {
@@ -86,13 +86,11 @@ function home() {
});
});
const at = room.indexOf('<section class="sc-room-scenes"');
const at = room.indexOf('<section class="sc-room-scenes"');
let body = at < 0 ? room.replace(/<\/div>$/, `${tiles}</div>`) : room.slice(0, at) + tiles + room.slice(at);
let body = at < 0 ? room.replace(/<\/div>$/, `${tiles}</div>`) : room.slice(0, at) + tiles + room.slice(at);
- // The home on a phone (the owner's short-pages rule, 2026-10-30): the three scenes are chips, one at a time, and
- // the questions are a fold. Both stay in the page for the crawlers.
+ // The home on a phone (the owner, 2026-10-31: never hide content to shorten a page): the three scenes are all shown,
+ // the chips jump to them, and the questions are a counted fold.
body = S.pickScenes(body, { key: 'scenes', label: 'What you can do', head: 'What you can do', labels: ['Ride maps', 'Surf the web', 'Watch'] });
body = S.pickScenes(body, { key: 'scenes', label: 'What you can do', head: 'What you can do', labels: ['Ride maps', 'Surf the web', 'Watch'] });
body = S.foldSection(body, 'id="sc-questions"', 'questions-fold');
body = S.foldSection(body, 'id="sc-questions"', 'questions-fold');
- // The rule that shows the picked scene: one per scene, so the CSS is written for this page.
- const rules = `@supports selector(:has(*)){@media (max-width:640px){${S.pickRules('scenes', ['scene-0', 'scene-1', 'scene-2'])}}}`;
- return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/', jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Surf', url: 'https://openvibe.surf/', description: 'Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.' }), seo.jsonLd.organization()], inlineStyles: [rules] });
+ return layout({ title: null, path: '/', body: `<div class="sf-wrap">${body}</div>`, active: '/', jsonLd: [seo.jsonLd.website({ name: 'OpenVibe.Surf', url: 'https://openvibe.surf/', description: 'Ride CS-style surf maps in the browser, surf what is live on OpenVibe.Live, and let an agent browse the web for you.' }), seo.jsonLd.organization()] });
}
}
function mapCard(m) {
function mapCard(m) {
@@ -25,15 +25,15 @@ function pickBlock({ key, label, head = '', panes, bodyClass = '' }) {
@@ -25,15 +25,15 @@ function pickBlock({ key, label, head = '', panes, bodyClass = '' }) {
// A pane with nothing to show is left out, so the chips and the rules that show them match the page.
// A pane with nothing to show is left out, so the chips and the rules that show them match the page.
panes = (panes || []).filter((p) => p.html);
panes = (panes || []).filter((p) => p.html);
if (!panes.length) return '';
if (!panes.length) return '';
- const chips = panes.map((p, i) => `<input type="radio" name="pick-${esc(key)}" id="pk-${esc(key)}-${esc(p.id)}" value="${esc(p.id)}"${i === 0 ? ' checked' : ''}><label for="pk-${esc(key)}-${esc(p.id)}">${esc(p.label)}</label>`).join('');
- // The pane carries the id the page already links to (an anchor in the hero, for instance), so a link to it still
- // opens that pane on a phone (the CSS shows the pane a link points at).
+ // Every pane is shown (the owner, 2026-10-31: never hide content to shorten a page); the chips are links that jump
+ // to a pane. They used to be radio chips that showed one pane and hid the others.
+ const chips = panes.map((p) => `<a class="pick-chip" href="#${esc(p.id)}">${esc(p.label)}</a>`).join('');
const body = panes.map((p) => `<div class="pick-pane" id="${esc(p.id)}">${p.html}</div>`).join('');
const body = panes.map((p) => `<div class="pick-pane" id="${esc(p.id)}">${p.html}</div>`).join('');
- const bar = `${head ? `<div class="pick-head"><h2>${esc(head)}</h2></div>` : ''}<div class="pick-row" role="radiogroup" aria-label="${esc(label)}">${chips}</div>`;
+ const bar = `${head ? `<div class="pick-head"><h2>${esc(head)}</h2></div>` : ''}<nav class="pick-row" aria-label="${esc(label)}">${chips}</nav>`;
return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar">${bar}</div><div class="pick-body${bodyClass ? ' ' + esc(bodyClass) : ''}">${body}</div></div>`;
return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar">${bar}</div><div class="pick-body${bodyClass ? ' ' + esc(bodyClass) : ''}">${body}</div></div>`;
}
}
-/** The rule that shows the picked block of a pick block: one per block, so the CSS is written for the page. */
+/** No longer needed (every pane shows): kept so a caller that still asks for the rules gets none. */
function pickRule(key, id) {
function pickRule(key, id) {
return `.pick:has(#pk-${key}-${id}:checked) #${id}{display:block}`;
return `.pick:has(#pk-${key}-${id}:checked) #${id}{display:block}`;
}
}
@@ -1,8 +1,8 @@
@@ -1,8 +1,8 @@
'use strict';
'use strict';
/**
/**
- * The home on a phone (the owner's short-pages rule, 2026-10-30): the three scenes are a pick block, a row of chips
- * picks one of them, and the questions are a fold. Radio inputs and :has(), no script for the chips; every scene stays
- * in the page for crawlers; a wider screen shows them all and no chips.
+ * The home on a phone (the owner, 2026-10-31: never hide content to shorten a page): the three scenes are all shown,
+ * a row of chips links to each, and the questions are a counted fold. The chips used to pick one scene and hide the
+ * others.
*/
*/
const assert = require('assert');
const assert = require('assert');
const fs = require('fs');
const fs = require('fs');
@@ -12,31 +12,28 @@ const { boot, check, done } = require('./helpers/boot');
@@ -12,31 +12,28 @@ const { boot, check, done } = require('./helpers/boot');
(async () => {
(async () => {
const t = await boot();
const t = await boot();
try {
try {
- await check('the scenes are chips: one per scene, the first picked, every scene in the page', async () => {
+ await check('every scene is shown; the chips are links that jump to one (the owner, 2026-10-31)', async () => {
const r = await t.get('/');
const r = await t.get('/');
assert.strictEqual(r.status, 200, r.text);
assert.strictEqual(r.status, 200, r.text);
const page = r.text;
const page = r.text;
- assert.ok(page.includes('<div class="pick" id="pick-scenes">'), 'the pick block is on the home');
+ assert.ok(page.includes('<div class="pick" id="pick-scenes">'), 'the scenes block is on the home');
const block = page.slice(page.indexOf('<div class="pick" id="pick-scenes">'));
const block = page.slice(page.indexOf('<div class="pick" id="pick-scenes">'));
- const chips = [...block.matchAll(/<input type="radio" name="pick-scenes" id="pk-scenes-(scene-\d+)" value="\1"( checked)?><label for="pk-scenes-\1">([^<]+)<\/label>/g)];
- assert.deepStrictEqual(chips.map((m) => m[1]), ['scene-0', 'scene-1', 'scene-2'], 'a chip per scene');
- assert.deepStrictEqual(chips.filter((m) => m[2]).map((m) => m[1]), ['scene-0'], 'the first scene is picked');
+ assert.ok(!/type="radio"/.test(block), 'no radio chips');
+ const chips = [...block.matchAll(/<a class="pick-chip" href="#(scene-\d+)">([^<]+)<\/a>/g)];
+ assert.deepStrictEqual(chips.map((m) => m[1]), ['scene-0', 'scene-1', 'scene-2'], 'a link per scene');
const panes = [...block.matchAll(/<div class="pick-pane" id="scene-(\d+)">/g)].map((m) => m[1]);
const panes = [...block.matchAll(/<div class="pick-pane" id="scene-(\d+)">/g)].map((m) => m[1]);
- assert.deepStrictEqual(panes, ['0', '1', '2'], 'every scene is in the page, one per chip');
- // Nothing was removed: the scenes and the doors are still there.
+ assert.deepStrictEqual(panes, ['0', '1', '2'], 'every scene is in the page');
for (const title of ['Press play, you are on the ramp', 'Surf the web with Vibe', 'Lean back and surf what is on', 'Ride', 'Web', 'Watch']) {
for (const title of ['Press play, you are on the ramp', 'Surf the web with Vibe', 'Lean back and surf what is on', 'Ride', 'Web', 'Watch']) {
assert.ok(page.includes(title), `${title} is on the page`);
assert.ok(page.includes(title), `${title} is on the page`);
}
}
});
});
- await check('the CSS guard: the chips are hidden and every scene shows until 640 px and where :has() is understood', async () => {
+ await check('the CSS: on a phone nothing is hidden; the chips are a row of 44 px links', async () => {
const r = await t.get('/');
const r = await t.get('/');
- assert.match(r.text, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-scenes-scene-0:checked\) #scene-0\{display:block\}/, 'the rule that shows the picked scene');
+ assert.ok(!/\.pick:has\(#pk-scenes/.test(r.text), 'no per-scene rule in the page');
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'surf.css'), 'utf8');
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'surf.css'), 'utf8');
- assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');
- assert.match(css, /@supports selector\(:has\(\*\)\) \{ @media \(max-width: 640px\) \{/, 'the phone rules are under :has() and 640 px');
- assert.match(css, /\.pick-row label \{[^}]*min-height: 44px/, 'a chip is a 44 px target');
- assert.match(css, /\.pick \.pick-pane \{ display: none; \}/, 'the scenes are hidden on a phone');
+ assert.ok(!/pick-pane \{ display: none/.test(css), 'no scene is hidden');
+ assert.match(css, /\.pick-chip \{[^}]*min-height: 44px/, 'a chip is a 44 px target');
assert.match(css, /\.sf-tiles \{ display: flex; overflow-x: auto;/, 'the three doors are one sideways row');
assert.match(css, /\.sf-tiles \{ display: flex; overflow-x: auto;/, 'the three doors are one sideways row');
});
});