From abe240073cc225fd87b4a24a039a97052a3b06ee Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 22 Oct 2026 17:21:14 +0200 Subject: [PATCH] feat: pass the Starlight gate on dense pages Pages of cards under one or two headings drop the table of contents, whose script watched every block of a 400-card page: 2 x 430 ms of main thread on a throttled phone. Tables are measured once the page is idle, skipping those in closed blocks, so a scrolling table is a tab stop anywhere on the page. The 12 heaviest pages score 97 to 100 in Lighthouse mobile, axe reports nothing on 16 pages in both themes, and the search answers the ten gate queries. --- scripts/sitestarlight.py | 9 +++++++-- website/src/scripts/site.ts | 15 ++++++++++++++- website/tools/check_tools.mjs | 3 +++ website/tools/lighthouse.py | 4 +++- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/scripts/sitestarlight.py b/scripts/sitestarlight.py index b146ad0f..60783b53 100644 --- a/scripts/sitestarlight.py +++ b/scripts/sitestarlight.py @@ -1,6 +1,6 @@ """The site as an Astro Starlight project: pages, sidebar and data files. -generate_site.py writes each page body; this module gives it the form +generate_site.py writes each page body, and this module gives it the form Starlight reads. A page's links are written as the docs tree holds them (``../systems/sony.md``, MkDocs' convention, the one a Markdown editor follows), and the site serves them as addresses under /retrobios/, so @@ -397,7 +397,12 @@ def finalize_page( if boost: front["boost"] = boost if rel.startswith(CARD_PAGES): - front["tableOfContents"] = {"minHeadingLevel": 2, "maxHeadingLevel": 2} + sections = len(re.findall(r"^## ", body, re.M)) + # A page of cards under one or two headings has nothing to list, and + # the table of contents watches every block of the page to follow + # the reader: 2 x 430 ms of a phone's main thread on 400 cards. + front["tableOfContents"] = ( + {"minHeadingLevel": 2, "maxHeadingLevel": 2} if sections > 2 else False) if not search: front["pagefind"] = False front["head"] = page_head(rel, level, front["title"], browser_title, structured) diff --git a/website/src/scripts/site.ts b/website/src/scripts/site.ts index d7e9fd97..68a426dc 100644 --- a/website/src/scripts/site.ts +++ b/website/src/scripts/site.ts @@ -131,7 +131,20 @@ const enhanceTables = () => { }, { rootMargin: '400px 0px' }, ); - tables.forEach((table) => observer.observe(table)); + // Every table is measured once the page is drawn and idle, before anything + // is written, so the read finds the layout done instead of forcing it: a + // keyboard reaches a scrolling table anywhere on the page, not only near + // the screen. + const idle = window.requestIdleCallback ?? ((callback: () => void) => setTimeout(callback, 1)); + const start = () => + idle(() => { + // A table in a closed block is not laid out, and measuring it would + // lay it out: it is measured when its block opens. + markScrollable(tables.filter((table) => table.checkVisibility?.() ?? true)); + tables.forEach((table) => observer.observe(table)); + }); + if (document.readyState === 'complete') start(); + else addEventListener('load', start, { once: true }); let timer = 0; addEventListener('resize', () => { clearTimeout(timer); diff --git a/website/tools/check_tools.mjs b/website/tools/check_tools.mjs index 2e843d8b..facb8d79 100644 --- a/website/tools/check_tools.mjs +++ b/website/tools/check_tools.mjs @@ -71,6 +71,9 @@ console.log('glossary term:', await term.textContent(), '=', await term.getAttri // On a phone, a table whose rows end in a download is one block per row. await page.setViewportSize({ width: 390, height: 844 }); await page.goto(`${base}which-pack/`, { waitUntil: 'networkidle' }); +// Tables are set up as they near the screen. +await page.locator('table:has(a[href*="/releases/download/"])').first().scrollIntoViewIfNeeded(); +await page.waitForTimeout(300); const stacked = await page.locator('table[data-rb-stack]').count(); const overflow = await page.evaluate(() => document.documentElement.scrollWidth > innerWidth + 1); console.log('stacked tables:', stacked, 'page wider than the phone:', overflow); diff --git a/website/tools/lighthouse.py b/website/tools/lighthouse.py index 3c8bbfce..e40ea638 100644 --- a/website/tools/lighthouse.py +++ b/website/tools/lighthouse.py @@ -42,7 +42,9 @@ def main() -> int: ["npx", "--prefix", str(Path(__file__).resolve().parent.parent), "lighthouse", base + page, "--quiet", "--output=json", f"--output-path={report.resolve()}", "--chrome-flags=--headless=new --no-sandbox", - f"--only-categories={','.join(categories)}"], + f"--only-categories={','.join(categories)}", + # Extra flags for one experiment, space separated. + *os.environ.get("LIGHTHOUSE_FLAGS", "").split()], env=env, cwd=out, check=False, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) data = json.loads(report.read_text()) cats, audits = data["categories"], data["audits"]