From 8d69ad54df108dc63d7106c798299087082498cc Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 11 Nov 2026 10:27:53 +0100 Subject: [PATCH] fix: space Starlight's previous and next links The word and the page title were split by a
only, which a copy, textContent and an assistant read as "PreviousWhat the packs do not hold". The override keeps Starlight's look, spaces them, names each link by direction and page, and makes the pair a navigation landmark. --- tests/test_site_nav.py | 4 +- tests/test_site_page_text.py | 37 ++++++++++- website/astro.config.mjs | 1 + website/src/components/Pagination.astro | 87 +++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 website/src/components/Pagination.astro diff --git a/tests/test_site_nav.py b/tests/test_site_nav.py index 7dc611aff..871631ea1 100644 --- a/tests/test_site_nav.py +++ b/tests/test_site_nav.py @@ -396,7 +396,7 @@ class _Page(HTMLParser): def handle_endtag(self, tag: str) -> None: if tag == "nav" and self._crumbs: self._crumbs = 0 - if tag == "div" and self._pagination and not self.pagination: + if tag == "nav" and self._pagination and not self.pagination: self._pagination = False if self._text is not None and tag in ("a", "summary"): self.sidebar.append(" ".join("".join(self._text).split())) @@ -621,7 +621,7 @@ def _html(url: str) -> str: def _pagination(text: str) -> tuple[str | None, str | None]: - start = text.find('
", start)] diff --git a/tests/test_site_page_text.py b/tests/test_site_page_text.py index d2c07212a..0aa4e3b52 100644 --- a/tests/test_site_page_text.py +++ b/tests/test_site_page_text.py @@ -44,13 +44,17 @@ PAGES = ("which-pack", "wiki/first-install", "wiki/architecture", "wiki/what-are "wiki/installer", "platforms/retrodeck", "provenance-no-intro", "explore", "index") BLOCKS = frozenset( - "address article aside blockquote br caption dd details dialog div dl dt fieldset figcaption " + "address article aside blockquote caption dd details dialog div dl dt fieldset figcaption " "figure footer form h1 h2 h3 h4 h5 h6 header hr li main nav ol option p pre section select " "summary table tbody td tfoot th thead tr ul".split()) VOID = frozenset("area base br col embed hr img input link meta source track wbr".split()) # What the stylesheet lays out as its own box beside the text before it: # the markup has to space it, a reader without the stylesheet sees no box. -OWN_BOX = ("rb-tile-name", "rb-tile-runs", "rb-stat-label", "rb-badge", "rb-table-filter-count") +# A
breaks a line on screen only: a copy and textContent join across +# it, so it separates nothing here. link-title is the page name of +# Starlight's previous and next links. +OWN_BOX = ("rb-tile-name", "rb-tile-runs", "rb-stat-label", "rb-badge", "rb-table-filter-count", + "link-title") class PageText(HTMLParser): @@ -272,6 +276,35 @@ class TheTwoFilterBoxesAreOne(unittest.TestCase): self.assertIn(".rb-table-filter[hidden] { visibility: hidden; }", css) +class PreviousAndNextAreSpaced(unittest.TestCase): + """Starlight's previous and next links read "PreviousWhat the packs do + not hold": the word and the title were split by a
only. The + override (src/components/Pagination.astro) spaces them and names each + link by its direction and its page.""" + + def test_the_override_is_registered(self): + config = (WEBSITE / "astro.config.mjs").read_text(encoding="utf-8") + self.assertIn("Pagination: './src/components/Pagination.astro',", config) + + def test_word_and_title_are_spaced_and_named(self): + source = (WEBSITE / "src" / "components" / "Pagination.astro").read_text(encoding="utf-8") + for word, rel, page in (("previousWord", "prev", "prev"), ("nextWord", "next", "next")): + with self.subTest(rel=rel): + self.assertIn(f"{{{word}}}{{' '}}\n\t\t\t\t\t
", source) + self.assertIn(f'rel="{rel}" aria-label={{`${{{word}}}: ${{{page}.label}}`}}', source) + self.assertRegex(source, r'
') + page = read(html, article_only=True) + self.assertEqual(page.glued, []) + self.assertEqual(page.text, "Previous A Next B") + self.assertTrue(read(html.replace("Previous
", "Previous
"), article_only=True).glued) + + class ThePickerWaitsForItsScript(unittest.TestCase): def test_hidden_until_the_setups_are_listed(self): source = (WEBSITE / "src" / "components" / "FilePicker.astro").read_text(encoding="utf-8") diff --git a/website/astro.config.mjs b/website/astro.config.mjs index 81b74bf95..4b7fd50db 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -105,6 +105,7 @@ export default defineConfig({ PageTitle: './src/components/PageTitle.astro', MarkdownContent: './src/components/MarkdownContent.astro', Footer: './src/components/Footer.astro', + Pagination: './src/components/Pagination.astro', Header: './src/components/Header.astro', Search: './src/components/Search.astro', }, diff --git a/website/src/components/Pagination.astro b/website/src/components/Pagination.astro new file mode 100644 index 000000000..809c225b6 --- /dev/null +++ b/website/src/components/Pagination.astro @@ -0,0 +1,87 @@ +--- +// Starlight's previous and next links (its Pagination.astro, 0.42), with a +// space after the word: the line break alone left "PreviousWhat the packs +// do not hold" in the page text a copy, a crawler or an assistant reads. +// Each link is named by its direction and its page, and the pair is a +// navigation landmark. +import { Icon } from '@astrojs/starlight/components'; + +const { dir, pagination } = Astro.locals.starlightRoute; +const { prev, next } = pagination; +const isRtl = dir === 'rtl'; +const previousWord = Astro.locals.t('page.previousLink'); +const nextWord = Astro.locals.t('page.nextLink'); +--- + + + +