diff --git a/tests/test_site_r2_css.py b/tests/test_site_r2_css.py index b2a968a7..928fca1d 100644 --- a/tests/test_site_r2_css.py +++ b/tests/test_site_r2_css.py @@ -156,11 +156,11 @@ class ThemeStylesheet(unittest.TestCase): permalink = next(b for s, b in _blocks(self.css) if s == ".rb-permalink") self.assertIn("min-height: 24px", permalink) - # 1.10, then round 3 item 1.3: one row that scrolls instead of two rows + # 1.10, then round 4 item 1.1: the row wraps, every tab on screen def test_os_tabs_stay_reachable(self): tablist = _rules(self.css, "starlight-tabs [role='tablist']")[0] - self.assertIn("flex-wrap: nowrap", tablist) - self.assertIn("overflow-x: auto", tablist) + self.assertIn("flex-wrap: wrap", tablist) + self.assertIn("overflow-x: visible", tablist) self.assertIn("white-space: nowrap", _rules(self.css, "starlight-tabs [role='tab']")[0]) # 1.11 @@ -176,7 +176,7 @@ class ThemeStylesheet(unittest.TestCase): self.assertIn("contain-intrinsic-size: auto 320px", cards) self.assertIn("--sl-content-width: 54rem", _rules(self.css, "body:has(.rb-sys-file, .rb-file-entry)")[0]) facts = _rules(self.css, "dl.rb-facts")[0] - self.assertIn("grid-template-columns: 9rem minmax(0, 1fr)", facts) + self.assertIn("grid-template-columns: 11rem minmax(0, 1fr)", facts) self.assertRegex(self.css, r"@media \(max-width: 30rem\) \{\s*\.sl-markdown-content dl\.rb-facts \{ grid-template-columns: minmax\(0, 1fr\)") # 1.13 diff --git a/tests/test_site_r3_css.py b/tests/test_site_r3_css.py index b78dde38..6607230b 100644 --- a/tests/test_site_r3_css.py +++ b/tests/test_site_r3_css.py @@ -2,7 +2,7 @@ Each test fails on the stylesheet as it was before the fix: commands allowed to break inside any word on a phone, a sidebar whose top links outsize its -group labels, OS tabs on two rows, badge links cut to a few pixels by the +group labels, badge links cut to a few pixels by the next row, 12 px hashes, Get your files tables each sizing their own columns. """ from __future__ import annotations @@ -44,18 +44,10 @@ class ThemeStylesheetRound3(unittest.TestCase): self.assertIn("font-size: var(--sl-text-sm)", links) self.assertIn("font-weight: 400", links) - # 1.3 - def test_os_tabs_on_one_row(self): - tablist = next(b for s, b in _blocks(self.css) if s == "starlight-tabs [role='tablist']") - self.assertIn("flex-wrap: nowrap", tablist) - self.assertIn("overflow-x: auto", tablist) - self.assertIn("scrollbar-width: none", tablist) - self.assertIn("display: none", _rules(self.css, "starlight-tabs [role='tablist']::-webkit-scrollbar")[0]) - # 1.4 def test_badge_links_keep_a_24px_target(self): dd = next(b for s, b in _blocks(self.css) if s == ".sl-markdown-content .rb-facts dd") - self.assertIn("line-height: 1.8", dd) + self.assertIn("line-height: 24px", dd) badge = _rules(self.css, ".rb-facts :is(.rb-badge:has(> a), a.rb-badge)")[0] for declaration in ("display: inline-flex", "align-items: center", "min-height: 24px", "margin-block: 2px"): with self.subTest(declaration=declaration): diff --git a/tests/test_site_r4_css.py b/tests/test_site_r4_css.py new file mode 100644 index 00000000..9c7b7527 --- /dev/null +++ b/tests/test_site_r4_css.py @@ -0,0 +1,84 @@ +"""Round 4 theme: the stylesheet fixes of package 1, read from its source. + +Each test fails on the stylesheet as it was before the fix: OS tabs on one +hidden scrolling row, stacked table cells 16 px apart with labels drawn over +the next row, short flags split across lines, lone card links 19 px high, a +card label column too narrow for its longest label. +""" +from __future__ import annotations + +import unittest + +from tests.test_site_r2_css import CSS_PATH, _blocks, _rules, _strip_comments + +STACK = ".sl-markdown-content table[data-rb-stack] td" + + +def _block(css: str, selector: str) -> str: + bodies = [b for s, b in _blocks(css) if s == selector] + if not bodies: + raise AssertionError(f"no block {selector}") + return "".join(bodies) + + +class ThemeStylesheetRound4(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.raw = CSS_PATH.read_text(encoding="utf-8") + cls.css = _strip_comments(cls.raw) + + # 1.1 + def test_os_tabs_wrap_and_never_hide(self): + tablist = _block(self.css, "starlight-tabs [role='tablist']") + for declaration in ("flex-wrap: wrap", "overflow-x: visible", "row-gap: 0.25rem"): + with self.subTest(declaration=declaration): + self.assertIn(declaration, tablist) + self.assertNotIn("scrollbar-width: none", self.css) + self.assertNotIn("::-webkit-scrollbar", self.css) + + # 1.2 + def test_stacked_cells_close_up_and_labels_push_rows(self): + cell = _block(self.css, STACK) + for declaration in ("margin-top: 0", "display: flow-root", "line-height: 1.5", + "padding: 0.125rem 0.6rem 0.125rem 8.4rem"): + with self.subTest(declaration=declaration): + self.assertIn(declaration, cell) + label = _block(self.css, STACK + "::before") + self.assertIn("position: static", label) + self.assertIn("float: left", label) + self.assertNotIn("position: absolute", label) + # The label sits in the cell's left padding: 8.4 - 0.6 rem. + self.assertIn("margin-left: -7.8rem", label) + + # 1.3 + def test_short_inline_code_never_splits(self): + self.assertIn("white-space: pre", _block(self.css, ".sl-markdown-content :not(pre) > code.rb-short")) + self.assertIn("white-space: break-spaces", _block(self.css, ".sl-markdown-content :not(pre) > code")) + self.assertIn("white-space: nowrap", _block(self.css, ".sl-markdown-content table.rb-options td:first-child")) + # A stacked value column is narrow: short code there moves down whole + # and splits only when wider than the column, never pushing the page. + stacked = _block(self.css, STACK + " code.rb-short") + for declaration in ("display: inline-block", "max-width: 100%", "overflow-wrap: anywhere"): + with self.subTest(declaration=declaration): + self.assertIn(declaration, stacked) + # Commands in code blocks keep wrapping on a phone. + self.assertIn("white-space: pre-wrap", _block(self.css, ".sl-markdown-content .expressive-code pre > code")) + + # 1.4 + def test_lone_card_links_are_24px_targets(self): + dd = _block(self.css, ".sl-markdown-content .rb-facts dd") + self.assertIn("min-height: 24px", dd) + self.assertIn("line-height: 24px", dd) + link = _block(self.css, ".sl-markdown-content .rb-facts dd > a") + self.assertIn("display: inline-block", link) + self.assertIn("min-height: 24px", link) + + # 1.5 + def test_card_labels_fit_and_lookup_labels_match(self): + facts = _rules(self.css, "dl.rb-facts")[0] + self.assertIn("grid-template-columns: 11rem minmax(0, 1fr)", facts) + self.assertIn("font-weight: 600", _block(self.css, ".sl-markdown-content .rb-file-lookup label")) + + +if __name__ == "__main__": + unittest.main() diff --git a/website/src/styles/retrobios.css b/website/src/styles/retrobios.css index 45c6637e..bbbf8eb2 100644 --- a/website/src/styles/retrobios.css +++ b/website/src/styles/retrobios.css @@ -467,14 +467,16 @@ body:has(.rb-sys-file, .rb-file-entry) { --sl-content-width: 54rem; } /* The facts of a file card: label column, value column, one column on a phone. */ .sl-markdown-content dl.rb-facts { display: grid; - grid-template-columns: 9rem minmax(0, 1fr); + grid-template-columns: 11rem minmax(0, 1fr); gap: 0.2rem 1rem; margin: 0.4rem 0 0; font-size: 0.85rem; } .sl-markdown-content .rb-facts :is(dt, dd) { margin: 0; padding: 0; } .sl-markdown-content .rb-facts dt { font-size: 0.8rem; font-weight: 400; color: var(--rb-text-secondary); } -.sl-markdown-content .rb-facts dd { min-width: 0; overflow-wrap: break-word; line-height: 1.8; } +.sl-markdown-content .rb-facts dd { min-width: 0; min-height: 24px; overflow-wrap: break-word; line-height: 24px; } +/* A lone link in a card value is a 24 px target too (WCAG 2.5.8). */ +.sl-markdown-content .rb-facts dd > a { display: inline-block; min-height: 24px; } /* A badge link in a card keeps a 24 px target, rows 4 px apart (WCAG 2.5.8). */ .sl-markdown-content .rb-facts :is(.rb-badge:has(> a), a.rb-badge) { display: inline-flex; @@ -577,6 +579,12 @@ abbr.rb-term[data-open]::after { white-space: normal; } +/* Inline code: a flag or a name of 30 characters or fewer (marked rb-short + at build time) never splits, longer code wraps at its spaces and keeps them. */ +.sl-markdown-content :not(pre) > code { white-space: break-spaces; } +.sl-markdown-content :not(pre) > code.rb-short { white-space: pre; } +.sl-markdown-content table.rb-options td:first-child { white-space: nowrap; } + /* Forms: lookup, picker, table filters */ .rb-file-lookup, .rb-picker fieldset { @@ -595,6 +603,8 @@ abbr.rb-term[data-open]::after { font: inherit; } .sl-markdown-content .rb-file-lookup input { flex: 1 1 20rem; font-family: var(--sl-font-mono); } +/* Both lookup labels, the hash and the file chooser, share one weight. */ +.sl-markdown-content .rb-file-lookup label { font-weight: 600; } .sl-markdown-content :is(.rb-file-lookup, .rb-picker) button { background: var(--rb-link); color: var(--sl-color-black); @@ -685,9 +695,8 @@ site-search .pagefind-ui__result-tags { display: none; } font-weight: 600; } -/* OS tabs stay on one row and scroll sideways on a narrow phone. */ -starlight-tabs [role='tablist'] { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; } -starlight-tabs [role='tablist']::-webkit-scrollbar { display: none; } +/* OS tabs wrap to a second row on a narrow phone: every tab stays on screen. */ +starlight-tabs [role='tablist'] { flex-wrap: wrap; overflow-x: visible; row-gap: 0.25rem; } starlight-tabs [role='tab'] { white-space: nowrap; } /* A page without "On this page" keeps the column, and the search box above @@ -719,21 +728,24 @@ starlight-tabs [role='tab'] { white-space: nowrap; } } .sl-markdown-content table[data-rb-stack] :is(tbody, tr) { display: block; } .sl-markdown-content table[data-rb-stack] tr { padding: 0.5rem 0; border-top: 1px solid var(--rb-border); } + /* A cell holds its label as a float in its left padding: a label on two + lines pushes the row down instead of running into the next one. No + sibling margin between cells. */ .sl-markdown-content table[data-rb-stack] td { - display: block; - position: relative; - min-height: 1.6rem; - padding: 0.25rem 0.6rem 0.25rem 8.4rem; + display: flow-root; + margin-top: 0; + padding: 0.125rem 0.6rem 0.125rem 8.4rem; border: 0; + line-height: 1.5; text-align: left !important; overflow-wrap: anywhere; } .sl-markdown-content table[data-rb-stack] td::before { content: attr(data-label); - position: absolute; - top: 0.25rem; - left: 0.6rem; + position: static; + float: left; width: 7.4rem; + margin-left: -7.8rem; font-weight: 600; color: var(--rb-text-secondary); } @@ -748,6 +760,14 @@ starlight-tabs [role='tab'] { white-space: nowrap; } font-weight: 600; color: var(--rb-text-secondary); } + /* A stacked value column is about 14rem wide: short code moves to the + next line whole, and splits only when it is wider than the column. */ + .sl-markdown-content table[data-rb-stack] td code.rb-short { + display: inline-block; + max-width: 100%; + white-space: pre-wrap; + overflow-wrap: anywhere; + } /* On a phone every command wraps: the end of a one-liner is never off-screen. A word is cut only when it cannot fit on a line. */