openvibers/openvibe.reviews Commits
Home: every pane shows on a phone; the chips are links, not a picker (#181)
Changes
@@@ -197,27 +197,22 @@ main .rv-h-panel-h { margin: 0 0 12px; padding: 0; border: 0; font-size: 1rem; }}--/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page shows one block 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; }--@supports selector(:has(*)) { @media (max-width: 640px) {-- .pick-bar { margin-top: 0; }++/* 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',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,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::-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; }-- .pick .pick-list > li { display: none; }-- /* The chip names the block, so the block's own heading gives way to it; the content stays in the page for the-- crawlers and for the reader on a wide screen. */-- .pick .sc-sec > h2, .pick section > h2, .pick > 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. */@@@ -29,7 +29,6 @@ const seo = require('openvibe-publishing/seo');const { renderPage, SITE_SUMMARY } = require('../render/layout');const views = require('../render/views');// The short home (the owner's rule, 2026-10-30): pick blocks and folds.--const { pickRules } = require('../render/short');const { ratingForStructuredData } = require('../reviews/aggregate');const { actorMiddleware, crossSite } = require('./common');const everywhere = require('../reviews/everywhere');@@@ -163,7 +162,6 @@ function createPages({ svc, platform, sync, viewers, config, log = console, limisend(req, res, 200, views.home({ entities, total: all.total, editor: isEditor(req), recent, kinds, stats, sites: everywhere.SITES.length, inspections, hazardous, complaints }), {robots: 'index, follow', cache: 'public', active: 'home', path: '/', summary: SITE_SUMMARY, styles: [showcase.STYLESHEET], description: HOME_DESCRIPTION,jsonLd: views.homeJsonLd({ base: config.baseUrl.replace(/\/+$/, ''), entities }),-- inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('records', recordIds)}}}`],});}));// What shipped on OpenVibe.Reviews: the shared update log every OpenVibe site has.@@@ -2,16 +2,14 @@/*** The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.*-- * pickBlock() several blocks of the same kind, one at a time on a phone, picked with a row of chips. Radio-- * inputs and :has(), no script: every block stays in the page for crawlers, and a wider screen (or a-- * browser without :has()) shows them all and no chips.-- * pickList() a list of things of the same kind, one at a time on a phone, picked with a row of chips.++ * pickBlock() several blocks of the same kind, all shown, each with its own heading; on a phone the chips are a++ * row of links that jump to them (the owner, 2026-10-31: never hide content to shorten).++ * pickList() a list of things of the same kind under one heading, every item shown.* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow* screen and remembers the reader's choice.*-- * The CSS for both lives in this site's own stylesheet; the rule that shows the picked block is written for the-- * page, because there is one per block.++ * The CSS lives in this site's own stylesheet.*/const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');@@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {// 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('');++ 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('');-- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div><div class="pick-body">' + body + '</div></div>';++ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><nav class="pick-row" aria-label="' + esc(label) + '">' + chips + '</nav></div><div class="pick-body">' + 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. */--function pickRule(key, id) {-- return '.pick:has(#pk-' + key + '-' + id + ':checked) #' + id + '{display:block}';--}--/** The rules for every block of a pick block, in the order they appear. */--function pickRules(key, ids) {-- return ids.map((id) => pickRule(key, id)).join('');--}/**-- * A pick list: a list of things of the same kind, one at a time on a phone, picked with a row of chips. The list-- * keeps its own markup and every item stays in the page; the caller marks each item with the index its chip picks.++ * A pick list: a list of things of the same kind under one heading, every item shown (2026-10-31). The list keeps++ * its own markup; `labels` is kept for callers and no longer drawn.** pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items*/function pickList({ key, label, head = '', labels, html }) {labels = labels || [];if (!labels.length) return '';-- const chips = labels.map((l, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + i + '" value="' + i + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + i + '">' + esc(l) + '</label>').join('');-- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div>' + html + '</div>';++ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div></div>' + html + '</div>';}--/** The rule that shows the picked item of a pick list: one per item, so the CSS is written for the page. */--function pickListRule(key, i) {-- return '.pick#pick-' + key + ':has(#pk-' + key + '-' + i + ':checked) .pick-list>li[data-i="' + i + '"]{display:block}';--}--/** The rules for every item of a pick list. */--function pickListRules(key, n) {-- return Array.from({ length: n }, (_, i) => pickListRule(key, i)).join('');--}/*** A fold. The head is the heading the block had; the html is what it holds.@@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);}--module.exports = { pickBlock, pickRule, pickRules, pickList, pickListRule, pickListRules, foldBlock, foldScript, part, swap, foldAround };++module.exports = { pickBlock, pickList, foldBlock, foldScript, part, swap, foldAround };@@@ -25,10 +25,10 @@ t('the records are one pick block: a chip per list, the first picked, every listconst start = page.indexOf('<div class="pick" id="pick-records">');assert.ok(start > 0, 'the pick block is on the home');const block = page.slice(start, page.indexOf('<details class="rv-h-fold"'));-- assert.match(block, /<div class="pick-row" role="radiogroup" aria-label="Public records">/);-- const chips = [...block.matchAll(/<input type="radio" name="pick-records" id="pk-records-([a-z-]+)" value="([a-z-]+)"( checked)?><label for="pk-records-[a-z-]+">([^<]+)<\/label>/g)];++ assert.match(block, /<nav class="pick-row" aria-label="Public records">/);++ const chips = [...block.matchAll(/<a class="pick-chip" href="#(?:pp-records-)?([a-z-]+)">()()([^<]+)<\/a>/g)];assert.strictEqual(chips.length, 3, `a chip per list (${chips.length})`);-- assert.deepStrictEqual(chips.filter((m) => m[3]).map((m) => m[1]), [chips[0][1]], 'the first list is picked');++ assert.ok(!/type="radio"/.test(block), 'no chip is a radio: nothing is picked, every pane shows');// Every list stays in the page, each in its own pane.for (const id of chips.map((m) => m[1])) assert.ok(block.includes(`id="${id}"`), `the pane of ${id} is in the page`);assert.ok(block.includes('The Coffee Club') && block.includes('Eldert Lane') && block.includes('TransUnion'), 'every list is in the page');@@@ -40,12 +40,8 @@ t('the CSS guard: the chips are hidden and the lists all show until 640 px and wtry {await importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: '06D', violation: 'Food contact surface not washed.', critical_flag: 'Critical' }));const page = (await req(h, 'GET', '/')).text;-- assert.match(page, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-records-rv-insp:checked\) #rv-insp\{display:block\}/, 'the rule that shows the picked list');const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'reviews.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 lists are hidden on a phone');} finally { await h.stop(); }});
@@ -197,27 +197,22 @@ main .rv-h-panel-h { margin: 0 0 12px; padding: 0; border: 0; font-size: 1rem; }
@@ -197,27 +197,22 @@ main .rv-h-panel-h { margin: 0 0 12px; padding: 0; border: 0; font-size: 1rem; }
}
}
-/* Pick (the owner's short-pages rule, 2026-10-30): on a phone the front page shows one block 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; }
-@supports selector(:has(*)) { @media (max-width: 640px) {
- .pick-bar { margin-top: 0; }
+/* 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: 700 clamp(20px,2.4vw,28px)/1.2 var(--ov-font,'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,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; }
- .pick .pick-list > li { display: none; }
- /* The chip names the block, so the block's own heading gives way to it; the content stays in the page for the
- crawlers and for the reader on a wide screen. */
- .pick .sc-sec > h2, .pick section > h2, .pick > 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. */
@@ -29,7 +29,6 @@ const seo = require('openvibe-publishing/seo');
@@ -29,7 +29,6 @@ const seo = require('openvibe-publishing/seo');
const { renderPage, SITE_SUMMARY } = require('../render/layout');
const { renderPage, SITE_SUMMARY } = require('../render/layout');
const views = require('../render/views');
const views = require('../render/views');
// 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 { pickRules } = require('../render/short');
const { ratingForStructuredData } = require('../reviews/aggregate');
const { ratingForStructuredData } = require('../reviews/aggregate');
const { actorMiddleware, crossSite } = require('./common');
const { actorMiddleware, crossSite } = require('./common');
const everywhere = require('../reviews/everywhere');
const everywhere = require('../reviews/everywhere');
@@ -163,7 +162,6 @@ function createPages({ svc, platform, sync, viewers, config, log = console, limi
@@ -163,7 +162,6 @@ function createPages({ svc, platform, sync, viewers, config, log = console, limi
send(req, res, 200, views.home({ entities, total: all.total, editor: isEditor(req), recent, kinds, stats, sites: everywhere.SITES.length, inspections, hazardous, complaints }), {
send(req, res, 200, views.home({ entities, total: all.total, editor: isEditor(req), recent, kinds, stats, sites: everywhere.SITES.length, inspections, hazardous, complaints }), {
robots: 'index, follow', cache: 'public', active: 'home', path: '/', summary: SITE_SUMMARY, styles: [showcase.STYLESHEET], description: HOME_DESCRIPTION,
robots: 'index, follow', cache: 'public', active: 'home', path: '/', summary: SITE_SUMMARY, styles: [showcase.STYLESHEET], description: HOME_DESCRIPTION,
jsonLd: views.homeJsonLd({ base: config.baseUrl.replace(/\/+$/, ''), entities }),
jsonLd: views.homeJsonLd({ base: config.baseUrl.replace(/\/+$/, ''), entities }),
- inlineStyles: [`@supports selector(:has(*)){@media (max-width:640px){${pickRules('records', recordIds)}}}`],
});
});
}));
}));
// What shipped on OpenVibe.Reviews: the shared update log every OpenVibe site has.
// What shipped on OpenVibe.Reviews: the shared update log every OpenVibe site has.
@@ -2,16 +2,14 @@
@@ -2,16 +2,14 @@
/**
/**
* The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.
* The home on a phone (the owner's short-pages rule, 2026-10-30): one or two screens, app-like.
*
*
- * pickBlock() several blocks of the same kind, one at a time on a phone, picked with a row of chips. Radio
- * inputs and :has(), no script: every block stays in the page for crawlers, and a wider screen (or a
- * browser without :has()) shows them all and no chips.
- * pickList() a list of things of the same kind, one at a time on a phone, picked with a row of chips.
+ * pickBlock() several blocks of the same kind, all shown, each with its own heading; on a phone the chips are a
+ * row of links that jump to them (the owner, 2026-10-31: never hide content to shorten).
+ * pickList() a list of things of the same kind under one heading, every item shown.
* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup
* foldBlock() a part a reader rarely needs, closed on a phone and open where there is room. The page's own markup
* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow
* keeps it open, so a browser without the script shows it as before; the script closes it on a narrow
* screen and remembers the reader's choice.
* screen and remembers the reader's choice.
*
*
- * The CSS for both lives in this site's own stylesheet; the rule that shows the picked block is written for the
- * page, because there is one per block.
+ * The CSS lives in this site's own stylesheet.
*/
*/
const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {
@@ -25,43 +23,26 @@ function pickBlock({ key, label, head = '', panes }) {
// 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('');
+ 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('');
- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div><div class="pick-body">' + body + '</div></div>';
+ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><nav class="pick-row" aria-label="' + esc(label) + '">' + chips + '</nav></div><div class="pick-body">' + 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. */
-function pickRule(key, id) {
- return '.pick:has(#pk-' + key + '-' + id + ':checked) #' + id + '{display:block}';
-}
-/** The rules for every block of a pick block, in the order they appear. */
-function pickRules(key, ids) {
- return ids.map((id) => pickRule(key, id)).join('');
-}
/**
/**
- * A pick list: a list of things of the same kind, one at a time on a phone, picked with a row of chips. The list
- * keeps its own markup and every item stays in the page; the caller marks each item with the index its chip picks.
+ * A pick list: a list of things of the same kind under one heading, every item shown (2026-10-31). The list keeps
+ * its own markup; `labels` is kept for callers and no longer drawn.
*
*
* pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items
* pickList({ key, label, head, labels, html }) labels: the chip labels, in the order of the items
*/
*/
function pickList({ key, label, head = '', labels, html }) {
function pickList({ key, label, head = '', labels, html }) {
labels = labels || [];
labels = labels || [];
if (!labels.length) return '';
if (!labels.length) return '';
- const chips = labels.map((l, i) => '<input type="radio" name="pick-' + esc(key) + '" id="pk-' + esc(key) + '-' + i + '" value="' + i + '"' + (i === 0 ? ' checked' : '') + '><label for="pk-' + esc(key) + '-' + i + '">' + esc(l) + '</label>').join('');
- return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div><div class="pick-row" role="radiogroup" aria-label="' + esc(label) + '">' + chips + '</div></div>' + html + '</div>';
+ return '<div class="pick" id="pick-' + esc(key) + '"><div class="pick-bar"><div class="pick-head"><h2>' + esc(head || label) + '</h2></div></div>' + html + '</div>';
}
}
-/** The rule that shows the picked item of a pick list: one per item, so the CSS is written for the page. */
-function pickListRule(key, i) {
- return '.pick#pick-' + key + ':has(#pk-' + key + '-' + i + ':checked) .pick-list>li[data-i="' + i + '"]{display:block}';
-}
-/** The rules for every item of a pick list. */
-function pickListRules(key, n) {
- return Array.from({ length: n }, (_, i) => pickListRule(key, i)).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.
@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {
@@ -106,4 +87,4 @@ function foldAround(html, needle, id, head) {
return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);
return html.slice(0, p.start) + foldBlock({ id, head, html: p.html }) + html.slice(p.end);
}
}
-module.exports = { pickBlock, pickRule, pickRules, pickList, pickListRule, pickListRules, foldBlock, foldScript, part, swap, foldAround };
+module.exports = { pickBlock, pickList, foldBlock, foldScript, part, swap, foldAround };
@@ -25,10 +25,10 @@ t('the records are one pick block: a chip per list, the first picked, every list
@@ -25,10 +25,10 @@ t('the records are one pick block: a chip per list, the first picked, every list
const start = page.indexOf('<div class="pick" id="pick-records">');
const start = page.indexOf('<div class="pick" id="pick-records">');
assert.ok(start > 0, 'the pick block is on the home');
assert.ok(start > 0, 'the pick block is on the home');
const block = page.slice(start, page.indexOf('<details class="rv-h-fold"'));
const block = page.slice(start, page.indexOf('<details class="rv-h-fold"'));
- assert.match(block, /<div class="pick-row" role="radiogroup" aria-label="Public records">/);
- const chips = [...block.matchAll(/<input type="radio" name="pick-records" id="pk-records-([a-z-]+)" value="([a-z-]+)"( checked)?><label for="pk-records-[a-z-]+">([^<]+)<\/label>/g)];
+ assert.match(block, /<nav class="pick-row" aria-label="Public records">/);
+ const chips = [...block.matchAll(/<a class="pick-chip" href="#(?:pp-records-)?([a-z-]+)">()()([^<]+)<\/a>/g)];
assert.strictEqual(chips.length, 3, `a chip per list (${chips.length})`);
assert.strictEqual(chips.length, 3, `a chip per list (${chips.length})`);
- assert.deepStrictEqual(chips.filter((m) => m[3]).map((m) => m[1]), [chips[0][1]], 'the first list is picked');
+ assert.ok(!/type="radio"/.test(block), 'no chip is a radio: nothing is picked, every pane shows');
// Every list stays in the page, each in its own pane.
// Every list stays in the page, each in its own pane.
for (const id of chips.map((m) => m[1])) assert.ok(block.includes(`id="${id}"`), `the pane of ${id} is in the page`);
for (const id of chips.map((m) => m[1])) assert.ok(block.includes(`id="${id}"`), `the pane of ${id} is in the page`);
assert.ok(block.includes('The Coffee Club') && block.includes('Eldert Lane') && block.includes('TransUnion'), 'every list is in the page');
assert.ok(block.includes('The Coffee Club') && block.includes('Eldert Lane') && block.includes('TransUnion'), 'every list is in the page');
@@ -40,12 +40,8 @@ t('the CSS guard: the chips are hidden and the lists all show until 640 px and w
@@ -40,12 +40,8 @@ t('the CSS guard: the chips are hidden and the lists all show until 640 px and w
try {
try {
await importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: '06D', violation: 'Food contact surface not washed.', critical_flag: 'Critical' }));
await importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: '06D', violation: 'Food contact surface not washed.', critical_flag: 'Critical' }));
const page = (await req(h, 'GET', '/')).text;
const page = (await req(h, 'GET', '/')).text;
- assert.match(page, /<style>@supports selector\(:has\(\*\)\)\{@media \(max-width:640px\)\{\.pick:has\(#pk-records-rv-insp:checked\) #rv-insp\{display:block\}/, 'the rule that shows the picked list');
const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'reviews.css'), 'utf8');
const css = require('fs').readFileSync(require('path').join(__dirname, '..', 'public', 'css', 'reviews.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 \.pick-pane \{ display: none; \}/, 'the lists are hidden on a phone');
} finally { await h.stop(); }
} finally { await h.stop(); }
});
});