openvibers/openvibe.zone Commits
Home: every pane shows on a phone; the chips are links, not a picker (#117)
Changes
@@@ -98,25 +98,22 @@ details input { font: inherit; padding: 7px 9px; border-radius: 8px; border: 1px/* Touch targets on phones (fleet audit 2026-10-29): the touch-only hit area for this group. Nothing moves with a mouse. */@media (pointer: coarse) { .zn-faq summary { min-height: 44px; } }--/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the home'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-block rule live under-- 640 px and only where :has() is understood, so a wider screen, or an older browser, shows every block and no chips. */--.pick-head { display: none; }--.pick-row { display: none; }--.pick-pane { min-width: 0; }--@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-size: 1.3rem; }++ .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-pane { display: none; }-- /* The chip names the block, so the block's own heading gives way to it. */-- .zn-pick-sec > h2 { 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. */@@@ -26,7 +26,7 @@ const { asyncRouter } = require('./router');const { createDiscoveryRoutes, homeJsonLd } = require('./discovery');const { sameOrigin } = require('./principal');const { html, raw } = require('../render/html');--const { pickBlock, pickRules, foldBlock } = require('../render/short');++const { pickBlock, foldBlock } = require('../render/short');const { send } = require('../render/layout');const { ZoneError, RESERVED } = require('../zone/zones');const { ClientError } = require('../zone/clients');@@@ -123,12 +123,8 @@ const zone = createZoneClient(createClient({ tokenProvider: tokens })); // a pconst z = await zone.zones.create('prj_…', { env: 'production', name: 'assets', quota_bytes: 512 * 1024 ** 2 });console.log(await zone.zones.usage(z.id));` },].map((c) => ({ id: c.id, label: c.label, html: String(html`<figure><figcaption>${c.label}</figcaption><pre tabindex="0" aria-label="${c.label}"><code${c.lang ? html` class="language-${c.lang}"` : ''}>${c.code}</code></pre></figure>`) }));-- // The rule that shows the picked sample is written for this page.-- const rules = [pickRules('tools', codePanes.map((p) => p.id))];return {agent: true, index: true, cache: me ? null : PUBLIC_CACHE, styles: [showcase.STYLESHEET],-- // The rules that show the picked block of each pick block live in the page's head.-- head: `<style>@supports selector(:has(*)){@media (max-width:640px){${rules.join('')}}</style>`,shipped: false,jsonLd: [...homeJsonLd(config), { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })) }],description: 'Private S3 buckets for your OpenVibe projects: create zones, issue access keys scoped to them, and use the AWS SDKs, rclone or any S3 tool.',@@@ -22,16 +22,13 @@ const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</*/function pickBlock({ key, label, head = '', headId = '', bodyClass = '', panes }) {if (!panes || !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('');++ const chips = panes.map((p) => `<a class="pick-chip" href="#pp-${esc(key)}-${esc(p.id)}">${esc(p.label)}</a>`).join('');const body = panes.map((p) => `<div class="pick-pane" id="pp-${esc(key)}-${esc(p.id)}">${p.html}</div>`).join('');const heading = `<h2${headId ? ` id="${esc(headId)}"` : ''}>${esc(head || label)}</h2>`;-- return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar"><div class="pick-head">${heading}</div><div class="pick-row" role="radiogroup" aria-label="${esc(label)}">${chips}</div></div><div class="pick-body${bodyClass ? ` ${bodyClass}` : ''}">${body}</div></div>`;++ return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar"><div class="pick-head">${heading}</div><nav class="pick-row" aria-label="${esc(label)}">${chips}</nav></div><div class="pick-body${bodyClass ? ` ${bodyClass}` : ''}">${body}</div></div>`;}/** The rules for every pane of a pick block, in the order they appear: one rule per pane, so the CSS is written for the page. */--function pickRules(key, ids) {-- return ids.map((id) => `.pick:has(#pk-${key}-${id}:checked) #pp-${key}-${id}{display:block}`).join('');--}/*** A fold. The head is the heading the block had; the html is what it holds.@@@ -47,4 +44,4 @@ function foldScript(id) {return `<script>(function(){var d=document.getElementById(${JSON.stringify(id)});if(!d)return;var k='ov_fold_'+${JSON.stringify(id)};var s=null;try{s=localStorage.getItem(k)}catch(e){}if(s==='open'){d.open=true;return}if(s==='closed'){d.open=false;return}var t=0;if(window.matchMedia&&window.matchMedia('(max-width:640px)').matches){t=Date.now();d.open=false}d.addEventListener('toggle',function(){if(Date.now()-t<300)return;try{localStorage.setItem(k,d.open?'open':'closed')}catch(e){}})})();</script>`;}--module.exports = { pickBlock, pickRules, foldBlock, foldScript };++module.exports = { pickBlock, foldBlock, foldScript };@@@ -25,17 +25,15 @@ const { boot, check, done } = require('./helpers/boot');// The code samples: one pick block, a chip per sample, the first one picked.assert.ok(text.includes('<div class="pick" id="pick-tools">'), 'the code samples are one pick block');-- assert.match(text, /<div class="pick-row" role="radiogroup" aria-label="The tools that reach a zone">/);-- const chips = [...text.matchAll(/<input type="radio" name="pick-tools" id="pk-tools-([a-z-]+)" value="\1"( checked)?><label for="pk-tools-\1">([^<]+)<\/label>/g)];++ assert.match(text, /<nav class="pick-row" aria-label="The tools that reach a zone">/);++ const chips = [...text.matchAll(/<a class="pick-chip" href="#(?:pp-tools-)?([a-z-]+)">()([^<]+)<\/a>/g)];assert.deepStrictEqual(chips.map((m) => m[1]), ['aws-cli', 'rclone', 'node', 'sdk'], 'a chip per sample');-- assert.deepStrictEqual(chips.filter((m) => m[2]).map((m) => m[1]), ['aws-cli'], 'the first sample is picked');++ assert.ok(!/type="radio"/.test(text), 'no chip is a radio: nothing is picked, every pane shows');assert.strictEqual((text.match(/class="pick-pane"/g) || []).length, chips.length, 'one pane per chip');// Every sample stays in the page for the crawlers.for (const label of ['AWS CLI', 'rclone', 'Node (AWS SDK v3)', 'Manage zones (openvibe-sdk)']) {assert.ok(text.includes(`<figcaption>${label}</figcaption>`), `${label} is in the page`);}-- // The rule that shows the picked sample is written for this page.-- assert.ok(text.includes('.pick:has(#pk-tools-aws-cli:checked) #pp-tools-aws-cli{display:block}'), 'the picked sample shows');// The folds: open in the page, closed on a phone, and their content stays in the page.for (const id of ['zn-what', 'zn-limits', 'zn-faq', 'zn-shipped']) {@@@ -46,12 +44,8 @@ const { boot, check, done } = require('./helpers/boot');assert.ok(text.includes('Which S3 operations work?'), 'the questions are still in the page');assert.ok(text.includes('ov-shipped-home'), 'what shipped is still in the page');-- // The CSS guard: the chips are hidden and the blocks all show until 640 px and where :has() is understood.const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'app.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-pane \{ display: none; \}/, 'the blocks are hidden on a phone');});await t.close();done();
@@ -98,25 +98,22 @@ details input { font: inherit; padding: 7px 9px; border-radius: 8px; border: 1px
@@ -98,25 +98,22 @@ details input { font: inherit; padding: 7px 9px; border-radius: 8px; border: 1px
/* Touch targets on phones (fleet audit 2026-10-29): the touch-only hit area for this group. Nothing moves with a mouse. */
/* Touch targets on phones (fleet audit 2026-10-29): the touch-only hit area for this group. Nothing moves with a mouse. */
@media (pointer: coarse) { .zn-faq summary { min-height: 44px; } }
@media (pointer: coarse) { .zn-faq summary { min-height: 44px; } }
-/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the home'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-block rule live under
- 640 px and only where :has() is understood, so a wider screen, or an older browser, shows every block and no chips. */
-.pick-head { display: none; }
-.pick-row { display: none; }
-.pick-pane { min-width: 0; }
-@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 { display: block; }
- .pick-head h2 { margin: 0 0 4px; font-size: 1.3rem; }
+ .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-pane { display: none; }
- /* The chip names the block, so the block's own heading gives way to it. */
- .zn-pick-sec > h2 { 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. */
@@ -26,7 +26,7 @@ const { asyncRouter } = require('./router');
@@ -26,7 +26,7 @@ const { asyncRouter } = require('./router');
const { createDiscoveryRoutes, homeJsonLd } = require('./discovery');
const { createDiscoveryRoutes, homeJsonLd } = require('./discovery');
const { sameOrigin } = require('./principal');
const { sameOrigin } = require('./principal');
const { html, raw } = require('../render/html');
const { html, raw } = require('../render/html');
-const { pickBlock, pickRules, foldBlock } = require('../render/short');
+const { pickBlock, foldBlock } = require('../render/short');
const { send } = require('../render/layout');
const { send } = require('../render/layout');
const { ZoneError, RESERVED } = require('../zone/zones');
const { ZoneError, RESERVED } = require('../zone/zones');
const { ClientError } = require('../zone/clients');
const { ClientError } = require('../zone/clients');
@@ -123,12 +123,8 @@ const zone = createZoneClient(createClient({ tokenProvider: tokens })); // a p
@@ -123,12 +123,8 @@ const zone = createZoneClient(createClient({ tokenProvider: tokens })); // a p
const z = await zone.zones.create('prj_…', { env: 'production', name: 'assets', quota_bytes: 512 * 1024 ** 2 });
const z = await zone.zones.create('prj_…', { env: 'production', name: 'assets', quota_bytes: 512 * 1024 ** 2 });
console.log(await zone.zones.usage(z.id));` },
console.log(await zone.zones.usage(z.id));` },
].map((c) => ({ id: c.id, label: c.label, html: String(html`<figure><figcaption>${c.label}</figcaption><pre tabindex="0" aria-label="${c.label}"><code${c.lang ? html` class="language-${c.lang}"` : ''}>${c.code}</code></pre></figure>`) }));
].map((c) => ({ id: c.id, label: c.label, html: String(html`<figure><figcaption>${c.label}</figcaption><pre tabindex="0" aria-label="${c.label}"><code${c.lang ? html` class="language-${c.lang}"` : ''}>${c.code}</code></pre></figure>`) }));
- // The rule that shows the picked sample is written for this page.
- const rules = [pickRules('tools', codePanes.map((p) => p.id))];
return {
return {
agent: true, index: true, cache: me ? null : PUBLIC_CACHE, styles: [showcase.STYLESHEET],
agent: true, index: true, cache: me ? null : PUBLIC_CACHE, styles: [showcase.STYLESHEET],
- // The rules that show the picked block of each pick block live in the page's head.
- head: `<style>@supports selector(:has(*)){@media (max-width:640px){${rules.join('')}}</style>`,
shipped: false,
shipped: false,
jsonLd: [...homeJsonLd(config), { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })) }],
jsonLd: [...homeJsonLd(config), { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })) }],
description: 'Private S3 buckets for your OpenVibe projects: create zones, issue access keys scoped to them, and use the AWS SDKs, rclone or any S3 tool.',
description: 'Private S3 buckets for your OpenVibe projects: create zones, issue access keys scoped to them, and use the AWS SDKs, rclone or any S3 tool.',
@@ -22,16 +22,13 @@ const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</
@@ -22,16 +22,13 @@ const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</
*/
*/
function pickBlock({ key, label, head = '', headId = '', bodyClass = '', panes }) {
function pickBlock({ key, label, head = '', headId = '', bodyClass = '', panes }) {
if (!panes || !panes.length) return '';
if (!panes || !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('');
+ const chips = panes.map((p) => `<a class="pick-chip" href="#pp-${esc(key)}-${esc(p.id)}">${esc(p.label)}</a>`).join('');
const body = panes.map((p) => `<div class="pick-pane" id="pp-${esc(key)}-${esc(p.id)}">${p.html}</div>`).join('');
const body = panes.map((p) => `<div class="pick-pane" id="pp-${esc(key)}-${esc(p.id)}">${p.html}</div>`).join('');
const heading = `<h2${headId ? ` id="${esc(headId)}"` : ''}>${esc(head || label)}</h2>`;
const heading = `<h2${headId ? ` id="${esc(headId)}"` : ''}>${esc(head || label)}</h2>`;
- return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar"><div class="pick-head">${heading}</div><div class="pick-row" role="radiogroup" aria-label="${esc(label)}">${chips}</div></div><div class="pick-body${bodyClass ? ` ${bodyClass}` : ''}">${body}</div></div>`;
+ return `<div class="pick" id="pick-${esc(key)}"><div class="pick-bar"><div class="pick-head">${heading}</div><nav class="pick-row" aria-label="${esc(label)}">${chips}</nav></div><div class="pick-body${bodyClass ? ` ${bodyClass}` : ''}">${body}</div></div>`;
}
}
/** The rules for every pane of a pick block, in the order they appear: one rule per pane, so the CSS is written for the page. */
/** The rules for every pane of a pick block, in the order they appear: one rule per pane, so the CSS is written for the page. */
-function pickRules(key, ids) {
- return ids.map((id) => `.pick:has(#pk-${key}-${id}:checked) #pp-${key}-${id}{display:block}`).join('');
-}
/**
/**
* A fold. The head is the heading the block had; the html is what it holds.
* A fold. The head is the heading the block had; the html is what it holds.
@@ -47,4 +44,4 @@ function foldScript(id) {
@@ -47,4 +44,4 @@ function foldScript(id) {
return `<script>(function(){var d=document.getElementById(${JSON.stringify(id)});if(!d)return;var k='ov_fold_'+${JSON.stringify(id)};var s=null;try{s=localStorage.getItem(k)}catch(e){}if(s==='open'){d.open=true;return}if(s==='closed'){d.open=false;return}var t=0;if(window.matchMedia&&window.matchMedia('(max-width:640px)').matches){t=Date.now();d.open=false}d.addEventListener('toggle',function(){if(Date.now()-t<300)return;try{localStorage.setItem(k,d.open?'open':'closed')}catch(e){}})})();</script>`;
return `<script>(function(){var d=document.getElementById(${JSON.stringify(id)});if(!d)return;var k='ov_fold_'+${JSON.stringify(id)};var s=null;try{s=localStorage.getItem(k)}catch(e){}if(s==='open'){d.open=true;return}if(s==='closed'){d.open=false;return}var t=0;if(window.matchMedia&&window.matchMedia('(max-width:640px)').matches){t=Date.now();d.open=false}d.addEventListener('toggle',function(){if(Date.now()-t<300)return;try{localStorage.setItem(k,d.open?'open':'closed')}catch(e){}})})();</script>`;
}
}
-module.exports = { pickBlock, pickRules, foldBlock, foldScript };
+module.exports = { pickBlock, foldBlock, foldScript };
@@ -25,17 +25,15 @@ const { boot, check, done } = require('./helpers/boot');
@@ -25,17 +25,15 @@ const { boot, check, done } = require('./helpers/boot');
// The code samples: one pick block, a chip per sample, the first one picked.
// The code samples: one pick block, a chip per sample, the first one picked.
assert.ok(text.includes('<div class="pick" id="pick-tools">'), 'the code samples are one pick block');
assert.ok(text.includes('<div class="pick" id="pick-tools">'), 'the code samples are one pick block');
- assert.match(text, /<div class="pick-row" role="radiogroup" aria-label="The tools that reach a zone">/);
- const chips = [...text.matchAll(/<input type="radio" name="pick-tools" id="pk-tools-([a-z-]+)" value="\1"( checked)?><label for="pk-tools-\1">([^<]+)<\/label>/g)];
+ assert.match(text, /<nav class="pick-row" aria-label="The tools that reach a zone">/);
+ const chips = [...text.matchAll(/<a class="pick-chip" href="#(?:pp-tools-)?([a-z-]+)">()([^<]+)<\/a>/g)];
assert.deepStrictEqual(chips.map((m) => m[1]), ['aws-cli', 'rclone', 'node', 'sdk'], 'a chip per sample');
assert.deepStrictEqual(chips.map((m) => m[1]), ['aws-cli', 'rclone', 'node', 'sdk'], 'a chip per sample');
- assert.deepStrictEqual(chips.filter((m) => m[2]).map((m) => m[1]), ['aws-cli'], 'the first sample is picked');
+ assert.ok(!/type="radio"/.test(text), 'no chip is a radio: nothing is picked, every pane shows');
assert.strictEqual((text.match(/class="pick-pane"/g) || []).length, chips.length, 'one pane per chip');
assert.strictEqual((text.match(/class="pick-pane"/g) || []).length, chips.length, 'one pane per chip');
// Every sample stays in the page for the crawlers.
// Every sample stays in the page for the crawlers.
for (const label of ['AWS CLI', 'rclone', 'Node (AWS SDK v3)', 'Manage zones (openvibe-sdk)']) {
for (const label of ['AWS CLI', 'rclone', 'Node (AWS SDK v3)', 'Manage zones (openvibe-sdk)']) {
assert.ok(text.includes(`<figcaption>${label}</figcaption>`), `${label} is in the page`);
assert.ok(text.includes(`<figcaption>${label}</figcaption>`), `${label} is in the page`);
}
}
- // The rule that shows the picked sample is written for this page.
- assert.ok(text.includes('.pick:has(#pk-tools-aws-cli:checked) #pp-tools-aws-cli{display:block}'), 'the picked sample shows');
// The folds: open in the page, closed on a phone, and their content stays in the page.
// The folds: open in the page, closed on a phone, and their content stays in the page.
for (const id of ['zn-what', 'zn-limits', 'zn-faq', 'zn-shipped']) {
for (const id of ['zn-what', 'zn-limits', 'zn-faq', 'zn-shipped']) {
@@ -46,12 +44,8 @@ const { boot, check, done } = require('./helpers/boot');
@@ -46,12 +44,8 @@ const { boot, check, done } = require('./helpers/boot');
assert.ok(text.includes('Which S3 operations work?'), 'the questions are still in the page');
assert.ok(text.includes('Which S3 operations work?'), 'the questions are still in the page');
assert.ok(text.includes('ov-shipped-home'), 'what shipped is still in the page');
assert.ok(text.includes('ov-shipped-home'), 'what shipped is still in the page');
- // The CSS guard: the chips are hidden and the blocks all show until 640 px and where :has() is understood.
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'app.css'), 'utf8');
const css = fs.readFileSync(path.join(__dirname, '..', 'public', 'css', 'app.css'), 'utf8');
assert.match(css, /\.pick-row \{ display: none; \}/, 'the chips are hidden by default');
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-pane \{ display: none; \}/, 'the blocks are hidden on a phone');
});
});
await t.close();
await t.close();
done();
done();