From 319ba2e1c3052cf4a8f19ae458c2a78bc7f51511 Mon Sep 17 00:00:00 2001 From: Lukas Heumos Date: Thu, 22 Oct 2026 10:32:32 +0200 Subject: [PATCH 1/6] feat: secondary accent for the sidebar captions Packages with a two-tone logo, like pertpy, want their second colour on the sidebar captions. The theme option secondary_accent sets it, defaulting to the package's second brand colour from the website, which scripts/sync_brand_tokens.py now writes to _secondary_accents.json. Like the accent, only a shade readable on the light and dark backgrounds reaches the page; without one the captions keep the text colour. pydata's --pst-color-secondary keeps following the accent, since it also colours link hovers, which should stay on the primary brand colour. --- docs/api/theme.md | 8 +++++ scripts/sync_brand_tokens.py | 18 ++++++++--- src/scverse_doc/_secondary_accents.json | 3 ++ src/scverse_doc/registry.py | 7 ++++ src/scverse_doc/theme/__init__.py | 32 +++++++++++++++---- .../theme/scverse/static/styles/scverse.css | 7 ++++ src/scverse_doc/theme/scverse/theme.toml | 1 + tests/test_build.py | 1 + tests/test_color.py | 18 +++++++++-- 9 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 src/scverse_doc/_secondary_accents.json diff --git a/docs/api/theme.md b/docs/api/theme.md index 062e617..a4965ca 100644 --- a/docs/api/theme.md +++ b/docs/api/theme.md @@ -32,6 +32,14 @@ The registry name to look the accent and the dropdown’s current entry up under CSS colour for decorative surfaces; link and text shades are derived from it. ``` +```{confval} secondary_accent +:type: str +:default: the registry’s second accent, else none + +CSS colour for the sidebar captions, for packages with a second brand colour; a readable shade is derived from it. +Without one, the captions keep the text colour. +``` + ```{confval} show_ecosystem_dropdown :type: bool :default: `True` diff --git a/scripts/sync_brand_tokens.py b/scripts/sync_brand_tokens.py index 08e2906..39f4ca5 100755 --- a/scripts/sync_brand_tokens.py +++ b/scripts/sync_brand_tokens.py @@ -14,7 +14,8 @@ which already carry dark values via ``light-dark()``, and defines only the brand hues itself. This script looks up the tokens the theme needs in the website’s vendored Bootstrap and ``assets/main.scss``, resolves every ``var()`` down to literals, and writes them into ``_tokens.css`` as ``--scverse-color-x``. -The per-package accents go to ``_accents.json``, because the registry needs their values in Python +The per-package accents go to ``_accents.json`` and the second brand colours to ``_secondary_accents.json``, +because the registry needs their values in Python to derive readable shades from them. Only the region between the marker comments is touched; the rest of the file is hand-authored. @@ -38,6 +39,7 @@ STATIC = HERE.parent / "src" / "scverse_doc" / "theme" / "scverse" / "static" TARGET = STATIC / "_tokens.css" ACCENTS_TARGET = HERE.parent / "src" / "scverse_doc" / "_accents.json" +SECONDARY_ACCENTS_TARGET = HERE.parent / "src" / "scverse_doc" / "_secondary_accents.json" #: Website file -> theme static file, copied verbatim. ASSETS = {Path("static/img/logo/scverse-fa.svg"): STATIC / "scverse-fa.svg"} @@ -75,6 +77,11 @@ "squidpy": "--squidpy-violet", } +#: Package -> website custom property holding its second brand colour, for packages that have one. +SECONDARY_ACCENT_MAP = { + "pertpy": "--pertpy-red", +} + BEGIN = "/* BEGIN GENERATED BRAND TOKENS – DO NOT EDIT; regenerate with scripts/sync_brand_tokens.py */" END = "/* END GENERATED BRAND TOKENS */" @@ -108,9 +115,9 @@ def render_region(props: dict[str, str], indent: str) -> str: return "\n".join(indent + line for line in lines) -def render_accents(props: dict[str, str]) -> str: - """Render the accent JSON read by ``scverse_doc.registry``.""" - return json.dumps({pkg: resolve(src, props) for pkg, src in ACCENT_MAP.items()}, indent=4) + "\n" +def render_accents(props: dict[str, str], accent_map: dict[str, str]) -> str: + """Render an accent JSON read by ``scverse_doc.registry``.""" + return json.dumps({pkg: resolve(src, props) for pkg, src in accent_map.items()}, indent=4) + "\n" def render(props: dict[str, str], current: str) -> str: @@ -133,7 +140,8 @@ def main() -> int: ) | parse_scss((args.website / "assets" / "main.scss").read_text()) updates = { TARGET: render(props, TARGET.read_text()).encode(), - ACCENTS_TARGET: render_accents(props).encode(), + ACCENTS_TARGET: render_accents(props, ACCENT_MAP).encode(), + SECONDARY_ACCENTS_TARGET: render_accents(props, SECONDARY_ACCENT_MAP).encode(), **{dst: (args.website / src).read_bytes() for src, dst in ASSETS.items()}, } if args.check: diff --git a/src/scverse_doc/_secondary_accents.json b/src/scverse_doc/_secondary_accents.json new file mode 100644 index 0000000..a3cff9b --- /dev/null +++ b/src/scverse_doc/_secondary_accents.json @@ -0,0 +1,3 @@ +{ + "pertpy": "#d2455e" +} diff --git a/src/scverse_doc/registry.py b/src/scverse_doc/registry.py index e9b047d..0c9d968 100644 --- a/src/scverse_doc/registry.py +++ b/src/scverse_doc/registry.py @@ -61,6 +61,9 @@ def _build_cache(app: Sphinx) -> Path: #: The website’s ``--scverse-deep-blue``, used when a package has no accent of its own. DEFAULT_ACCENT = _ACCENTS.pop("default") +#: Second brand colours from the website, for the packages that have one. +_SECONDARY_ACCENTS: dict[str, str] = json.loads(files(__package__).joinpath("_secondary_accents.json").read_text()) + #: Upstream ``category`` -> the ``kind`` recorded here. #: ``core-infrastructure`` is skipped: #: those entries are repositories (governance, stats, the website), not packages. @@ -105,6 +108,9 @@ class Package: accent: str = DEFAULT_ACCENT """The package’s brand accent, falling back to the scverse deep blue.""" + secondary_accent: str | None = None + """The package’s second brand colour, or :data:`None` if it has only one.""" + class _Entry(TypedDict): """The fields of an upstream listing entry that the registry reads.""" @@ -149,6 +155,7 @@ def _load() -> Mapping[str, Package]: repo=meta.get("project_home"), description=" ".join(meta.get("description", "").split()), accent=_ACCENTS.get(name, DEFAULT_ACCENT), + secondary_accent=_SECONDARY_ACCENTS.get(name), ) # Sorted here, because upstream’s listing order is not stable. for name, meta in sorted(((meta["name"].casefold(), meta) for meta in _fetch()), key=itemgetter(0)) diff --git a/src/scverse_doc/theme/__init__.py b/src/scverse_doc/theme/__init__.py index 5d41aae..ee25987 100644 --- a/src/scverse_doc/theme/__init__.py +++ b/src/scverse_doc/theme/__init__.py @@ -38,6 +38,12 @@ }} """ +_SECONDARY_ACCENT_CSS = """\ +:root {{ + --scverse-color-secondary-accent-text: light-dark({light}, {dark}); +}} +""" + #: scverse.org itself is linked from the scverse menu beside them. _ICON_LINKS = ({"name": "Discourse", "url": "https://discourse.scverse.org/", "icon": "fa-brands fa-discourse"},) @@ -54,6 +60,14 @@ def _accent(config: Config) -> str: return DEFAULT_ACCENT +def _secondary_accent(config: Config) -> str | None: + if accent := config.html_theme_options.get("secondary_accent"): + return str(accent) + if (pkg := packages.get(_package_name(config))) is not None: + return pkg.secondary_accent + return None + + def _add_chrome(config: Config) -> None: """Add the brand icon links and the colour mode, alongside whatever :mod:`scverse_doc.source` set.""" options = config.html_theme_options @@ -79,15 +93,19 @@ def _configure(app: Sphinx) -> None: _add_chrome(config) accent = _accent(config) + css = _ACCENT_CSS.format( + accent=accent, + light=derive_readable(accent, _LIGHT_BACKGROUND), + dark=derive_readable(accent, _DARK_BACKGROUND), + ) + if secondary := _secondary_accent(config): + css += _SECONDARY_ACCENT_CSS.format( + light=derive_readable(secondary, _LIGHT_BACKGROUND), + dark=derive_readable(secondary, _DARK_BACKGROUND), + ) static_dir = _build_cache(app) / "static" static_dir.mkdir(parents=True, exist_ok=True) - (static_dir / "scverse-accent.css").write_text( - _ACCENT_CSS.format( - accent=accent, - light=derive_readable(accent, _LIGHT_BACKGROUND), - dark=derive_readable(accent, _DARK_BACKGROUND), - ) - ) + (static_dir / "scverse-accent.css").write_text(css) config.html_static_path = [*config.html_static_path, str(static_dir)] app.add_css_file("scverse-accent.css") app.add_js_file("scripts/readthedocs-integration.js", type="module") diff --git a/src/scverse_doc/theme/scverse/static/styles/scverse.css b/src/scverse_doc/theme/scverse/static/styles/scverse.css index cb23964..32fab73 100644 --- a/src/scverse_doc/theme/scverse/static/styles/scverse.css +++ b/src/scverse_doc/theme/scverse/static/styles/scverse.css @@ -228,6 +228,13 @@ a.dropdown-item { } } +nav.bd-links p.caption { + color: var( + --scverse-color-secondary-accent-text, + var(--pst-color-text-base) + ); +} + readthedocs-flyout { --readthedocs-flyout-background-color: transparent; --readthedocs-flyout-font-family: inherit; diff --git a/src/scverse_doc/theme/scverse/theme.toml b/src/scverse_doc/theme/scverse/theme.toml index 336c8ab..5bae955 100644 --- a/src/scverse_doc/theme/scverse/theme.toml +++ b/src/scverse_doc/theme/scverse/theme.toml @@ -9,6 +9,7 @@ sidebars = ["navbar-logo", "search-button-field", "scverse-sidebar-nav"] [options] package = "" accent = "" +secondary_accent = "" show_ecosystem_dropdown = "True" # No navbar: its parts live in the primary sidebar and the article header. navbar_start = "" diff --git a/tests/test_build.py b/tests/test_build.py index b50788b..b39fd5f 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -80,6 +80,7 @@ def test_accent_is_derived_for_both_modes(minimal: tuple[SphinxTestApp, str]) -> css = (Path(app.outdir) / "_static" / "scverse-accent.css").read_text() assert "--scverse-color-accent-decorative: #30679a;" in css assert "--scverse-color-accent-text: light-dark(#" in css + assert "--scverse-color-secondary-accent-text: light-dark(#" in css @needs_full diff --git a/tests/test_color.py b/tests/test_color.py index 4aa2047..f0e8eb5 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -9,9 +9,9 @@ from scverse_doc._color import WCAG_AA_NORMAL, contrast_ratio, derive_readable, parse_hex from scverse_doc.registry import DEFAULT_ACCENT, packages -from scverse_doc.theme import _DARK_BACKGROUND, _LIGHT_BACKGROUND, _accent +from scverse_doc.theme import _DARK_BACKGROUND, _LIGHT_BACKGROUND, _accent, _secondary_accent -ACCENTS = sorted({pkg.accent for pkg in packages.values()}) +ACCENTS = sorted({a for pkg in packages.values() for a in (pkg.accent, pkg.secondary_accent) if a}) def test_every_derived_accent_meets_aa() -> None: @@ -55,3 +55,17 @@ def test_unreachable_target_falls_back_to_the_extreme(background: str, expected: def test_accent_for_a_package_outside_the_registry(options: dict[str, str], expected: str) -> None: config = SimpleNamespace(html_theme_options=options, project="not-an-scverse-package") assert _accent(config) == expected # type: ignore[arg-type] + + +@pytest.mark.parametrize( + ("project", "options", "expected"), + [ + pytest.param("not-an-scverse-package", {}, None, id="unknown"), + pytest.param("anndata", {}, None, id="single-colour"), + pytest.param("pertpy", {}, "#d2455e", id="registry"), + pytest.param("pertpy", {"secondary_accent": "#c0f"}, "#c0f", id="declared"), + ], +) +def test_secondary_accent(project: str, options: dict[str, str], expected: str | None) -> None: + config = SimpleNamespace(html_theme_options=options, project=project) + assert _secondary_accent(config) == expected # type: ignore[arg-type] From 2810dde147142400e8e71688bc52572d7a6454fa Mon Sep 17 00:00:00 2001 From: Lukas Heumos Date: Thu, 22 Oct 2026 10:33:28 +0200 Subject: [PATCH 2/6] docs: add changelog entry for #73 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c56647..3c91677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning][]. ## [Unreleased] +### Added + +- `secondary_accent` theme option for the sidebar captions, defaulting to a package’s second brand colour from the website (#73) + ### Fixed - header shadow now only extends downward (#70) From ff147d818f6ec0a6bb1ea5700215b1b4bed3c1ae Mon Sep 17 00:00:00 2001 From: Lukas Heumos Date: Thu, 22 Oct 2026 15:52:29 +0200 Subject: [PATCH 3/6] fix: read the brand accents from the website's accents.json scverse/scverse.github.io#368 moved the package colours into assets/accents.json and generates the --- properties from it in Sass, so main.scss no longer spells them out and the sync failed with --scverse-deep-blue and --scverse-sky-blue missing. The script now reads accents.json, converts its OKLCH values to sRGB hex, takes each package's first colour as its accent and the second colour for the packages opted in to secondary accents, so far only pertpy. The generated files are byte-identical except spatialdata, whose rounded OKLCH chroma of 0.2 lies outside sRGB and clips to #00a8ff instead of #40a9ff; 0.158 restores it, so that is fixed on the website rather than here. --- scripts/sync_brand_tokens.py | 74 ++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/scripts/sync_brand_tokens.py b/scripts/sync_brand_tokens.py index 39f4ca5..1fdff6d 100755 --- a/scripts/sync_brand_tokens.py +++ b/scripts/sync_brand_tokens.py @@ -12,10 +12,11 @@ The website takes its neutral colours from Bootstrap’s tokens (``--bs-fg-*``, ``--bs-bg-*``, …), which already carry dark values via ``light-dark()``, and defines only the brand hues itself. -This script looks up the tokens the theme needs in the website’s vendored Bootstrap and ``assets/main.scss``, -resolves every ``var()`` down to literals, and writes them into ``_tokens.css`` as ``--scverse-color-x``. -The per-package accents go to ``_accents.json`` and the second brand colours to ``_secondary_accents.json``, -because the registry needs their values in Python +This script looks up the tokens the theme needs in the website’s vendored Bootstrap, ``assets/main.scss`` +and ``assets/accents.json``, which lists each package’s brand colours in OKLCH, primary first. +It resolves every ``var()`` down to literals and writes them into ``_tokens.css`` as ``--scverse-color-x``. +The per-package accents go to ``_accents.json`` and the opted-in second brand colours to ``_secondary_accents.json`` +as sRGB hex, because the registry needs their values in Python to derive readable shades from them. Only the region between the marker comments is touched; the rest of the file is hand-authored. @@ -31,6 +32,7 @@ import argparse import json +import math import re import sys from pathlib import Path @@ -63,24 +65,11 @@ "--scverse-color-footer-bg": "--bs-bg-2", } -#: Package -> website custom property holding its accent; ``default`` is for packages without one. -ACCENT_MAP = { - "default": "--scverse-deep-blue", - "anndata": "--anndata-orange", - "mudata": "--mudata-green", - "muon": "--muon-aquamarine", - "pertpy": "--pertpy-blue", - "scanpy": "--scanpy-cerise", - "scirpy": "--scirpy-purple", - "scvi-tools": "--scvi-yellow", - "spatialdata": "--spatialdata-blue", - "squidpy": "--squidpy-violet", -} +#: Website ``accents.json`` key -> registry name, where they differ; ``default`` is for packages without an accent. +PACKAGE_NAMES = {"scverse": "default", "scvi": "scvi-tools"} -#: Package -> website custom property holding its second brand colour, for packages that have one. -SECONDARY_ACCENT_MAP = { - "pertpy": "--pertpy-red", -} +#: Packages whose second website colour is used for the sidebar captions. +SECONDARY_ACCENT_PACKAGES = frozenset({"pertpy"}) BEGIN = "/* BEGIN GENERATED BRAND TOKENS – DO NOT EDIT; regenerate with scripts/sync_brand_tokens.py */" END = "/* END GENERATED BRAND TOKENS */" @@ -101,6 +90,28 @@ def parse_scss(scss: str) -> dict[str, str]: return dict(DECL_RE.findall(scss)) +def oklch_to_hex(lightness: float, chroma: float, hue: float) -> str: + """Convert an OKLCH colour to sRGB hex, clipping out-of-gamut channels as browsers do on sRGB screens.""" + a, b = chroma * math.cos(math.radians(hue)), chroma * math.sin(math.radians(hue)) + long = (lightness + 0.3963377774 * a + 0.2158037573 * b) ** 3 + medium = (lightness - 0.1055613458 * a - 0.0638541728 * b) ** 3 + short = (lightness - 0.0894841775 * a - 1.2914855480 * b) ** 3 + linear = ( + 4.0767416621 * long - 3.3077115913 * medium + 0.2309699292 * short, + -1.2684380046 * long + 2.6097574011 * medium - 0.3413193965 * short, + -0.0041960863 * long - 0.7034186147 * medium + 1.7076147010 * short, + ) + srgb = ( + 12.92 * c if c <= 0.0031308 else 1.055 * c ** (1 / 2.4) - 0.055 for c in (min(max(c, 0), 1) for c in linear) + ) + return "#" + "".join(f"{round(c * 255):02x}" for c in srgb) + + +def parse_accents(raw: str) -> dict[str, dict[str, str]]: + """Map each package in the website’s ``accents.json`` to its colour names and hex values, primary first.""" + return {pkg: {name: oklch_to_hex(*lch) for name, lch in colours} for pkg, colours in json.loads(raw).items()} + + def resolve(name: str, props: dict[str, str]) -> str: """Return the value of `name` with every ``var()`` in it recursively substituted.""" return re.sub(r"var\((--[\w-]+)\)", lambda m: resolve(m[1], props), props[name]) @@ -115,9 +126,9 @@ def render_region(props: dict[str, str], indent: str) -> str: return "\n".join(indent + line for line in lines) -def render_accents(props: dict[str, str], accent_map: dict[str, str]) -> str: - """Render an accent JSON read by ``scverse_doc.registry``.""" - return json.dumps({pkg: resolve(src, props) for pkg, src in accent_map.items()}, indent=4) + "\n" +def render_accents(accents: dict[str, str]) -> str: + """Render an accent JSON read by ``scverse_doc.registry``, ``default`` first.""" + return json.dumps(dict(sorted(accents.items(), key=lambda kv: (kv[0] != "default", kv[0]))), indent=4) + "\n" def render(props: dict[str, str], current: str) -> str: @@ -135,13 +146,18 @@ def main() -> int: parser.add_argument("--check", action="store_true", help="fail instead of writing if the output would change") args = parser.parse_args() - props = parse_bootstrap( - (args.website / "static" / "bootstrap" / "css" / "bootstrap.min.css").read_text() - ) | parse_scss((args.website / "assets" / "main.scss").read_text()) + accents = parse_accents((args.website / "assets" / "accents.json").read_text()) + props = ( + parse_bootstrap((args.website / "static" / "bootstrap" / "css" / "bootstrap.min.css").read_text()) + | parse_scss((args.website / "assets" / "main.scss").read_text()) + | {f"--{pkg}-{name}": colour for pkg, colours in accents.items() for name, colour in colours.items()} + ) + primary = {PACKAGE_NAMES.get(pkg, pkg): next(iter(colours.values())) for pkg, colours in accents.items()} + secondary = {pkg: list(accents[pkg].values())[1] for pkg in SECONDARY_ACCENT_PACKAGES} updates = { TARGET: render(props, TARGET.read_text()).encode(), - ACCENTS_TARGET: render_accents(props, ACCENT_MAP).encode(), - SECONDARY_ACCENTS_TARGET: render_accents(props, SECONDARY_ACCENT_MAP).encode(), + ACCENTS_TARGET: render_accents(primary).encode(), + SECONDARY_ACCENTS_TARGET: render_accents(secondary).encode(), **{dst: (args.website / src).read_bytes() for src, dst in ASSETS.items()}, } if args.check: From 02b3b135ff076bc7e120aba3d29345b3243e8197 Mon Sep 17 00:00:00 2001 From: Lukas Heumos Date: Thu, 22 Oct 2026 16:52:33 +0200 Subject: [PATCH 4/6] feat: opt in to the secondary accent from conf.py Which packages colour their sidebar captions is now each package's choice: secondary_accent = True takes its second brand colour from the registry instead of every package with a second colour getting coloured captions. --- CHANGELOG.md | 2 +- docs/api/theme.md | 7 ++++--- src/scverse_doc/theme/__init__.py | 10 +++++----- tests/roots/minimal/conf.py | 2 +- tests/test_build.py | 2 +- tests/test_color.py | 7 ++++--- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dc96d0..ce6f3c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ and this project adheres to [Semantic Versioning][]. ### Added -- `secondary_accent` theme option for the sidebar captions, defaulting to a package’s second brand colour from the website (#73) +- `secondary_accent` theme option for the sidebar captions, which `True` sets to the package’s second brand colour from the website (#73) ### Changed diff --git a/docs/api/theme.md b/docs/api/theme.md index a4965ca..c302b77 100644 --- a/docs/api/theme.md +++ b/docs/api/theme.md @@ -33,10 +33,11 @@ CSS colour for decorative surfaces; link and text shades are derived from it. ``` ```{confval} secondary_accent -:type: str -:default: the registry’s second accent, else none +:type: str | bool +:default: none -CSS colour for the sidebar captions, for packages with a second brand colour; a readable shade is derived from it. +CSS colour for the sidebar captions; a readable shade is derived from it. +`True` takes the package’s second brand colour from the registry, if it has one. Without one, the captions keep the text colour. ``` diff --git a/src/scverse_doc/theme/__init__.py b/src/scverse_doc/theme/__init__.py index 7de4e40..b867676 100644 --- a/src/scverse_doc/theme/__init__.py +++ b/src/scverse_doc/theme/__init__.py @@ -71,11 +71,11 @@ def _accent(config: Config) -> str: def _secondary_accent(config: Config) -> str | None: - if accent := config.html_theme_options.get("secondary_accent"): - return str(accent) - if (pkg := packages.get(_package_name(config))) is not None: - return pkg.secondary_accent - return None + accent = config.html_theme_options.get("secondary_accent") + if str(accent).casefold() == "true": + pkg = packages.get(_package_name(config)) + return None if pkg is None else pkg.secondary_accent + return str(accent) if accent else None def _add_chrome(config: Config) -> None: diff --git a/tests/roots/minimal/conf.py b/tests/roots/minimal/conf.py index 4152546..a6b5146 100644 --- a/tests/roots/minimal/conf.py +++ b/tests/roots/minimal/conf.py @@ -3,4 +3,4 @@ extensions = ["scverse_doc", "sphinx.ext.linkcode"] source_repository = "https://github.com/scverse/pertpy" source_branch = "main" -html_theme_options = {"announcement": ""} +html_theme_options = {"announcement": "", "secondary_accent": True} diff --git a/tests/test_build.py b/tests/test_build.py index 21f083f..8ea53b3 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -126,7 +126,7 @@ def test_conf_py_can_pick_another_theme(tmp_path: Path) -> None: """A theme that declares none of the source options must not be given any.""" app, _ = build(ROOTS / "minimal", tmp_path, html_theme="alabaster") assert app.config.html_theme == "alabaster" - assert app.config.html_theme_options.keys() == {"announcement"} + assert app.config.html_theme_options.keys() == {"announcement", "secondary_accent"} @needs_full diff --git a/tests/test_color.py b/tests/test_color.py index b7094e1..41c1333 100644 --- a/tests/test_color.py +++ b/tests/test_color.py @@ -66,9 +66,10 @@ def test_accent_for_a_package_outside_the_registry(options: dict[str, str], expe @pytest.mark.parametrize( ("project", "options", "expected"), [ - pytest.param("not-an-scverse-package", {}, None, id="unknown"), - pytest.param("anndata", {}, None, id="single-colour"), - pytest.param("pertpy", {}, "oklch(0.595 0.177 14)", id="registry"), + pytest.param("pertpy", {}, None, id="unset"), + pytest.param("pertpy", {"secondary_accent": True}, "oklch(0.595 0.177 14)", id="registry"), + pytest.param("anndata", {"secondary_accent": True}, None, id="single-colour"), + pytest.param("not-an-scverse-package", {"secondary_accent": True}, None, id="unknown"), pytest.param("pertpy", {"secondary_accent": "#c0f"}, "#c0f", id="declared"), ], ) From 69dbd924bcd8659b0fca245812aa80c617037bd2 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Thu, 22 Oct 2026 17:20:27 +0200 Subject: [PATCH 5/6] types --- src/scverse_doc/theme/__init__.py | 11 ++++++----- src/scverse_doc/theme/scverse/theme.toml | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/scverse_doc/theme/__init__.py b/src/scverse_doc/theme/__init__.py index b867676..f41be5b 100644 --- a/src/scverse_doc/theme/__init__.py +++ b/src/scverse_doc/theme/__init__.py @@ -71,11 +71,12 @@ def _accent(config: Config) -> str: def _secondary_accent(config: Config) -> str | None: - accent = config.html_theme_options.get("secondary_accent") - if str(accent).casefold() == "true": - pkg = packages.get(_package_name(config)) - return None if pkg is None else pkg.secondary_accent - return str(accent) if accent else None + accent: str | bool = config.html_theme_options.get("secondary_accent") + if not accent: + return None + if pkg := packages.get(_package_name(config)): + return pkg.secondary_accent + return str(accent) def _add_chrome(config: Config) -> None: diff --git a/src/scverse_doc/theme/scverse/theme.toml b/src/scverse_doc/theme/scverse/theme.toml index 5bae955..7b51404 100644 --- a/src/scverse_doc/theme/scverse/theme.toml +++ b/src/scverse_doc/theme/scverse/theme.toml @@ -9,23 +9,23 @@ sidebars = ["navbar-logo", "search-button-field", "scverse-sidebar-nav"] [options] package = "" accent = "" -secondary_accent = "" -show_ecosystem_dropdown = "True" +secondary_accent = false +show_ecosystem_dropdown = true # No navbar: its parts live in the primary sidebar and the article header. -navbar_start = "" -navbar_center = "" -navbar_end = "" -navbar_persistent = "" +navbar_start = [] +navbar_center = [] +navbar_end = [] +navbar_persistent = [] article_header_start = ["toggle-primary-sidebar", "breadcrumbs"] article_header_end = ["theme-switcher", "navbar-icon-links", "scverse-ecosystem", "toggle-secondary-sidebar"] primary_sidebar_end = ["sidebar-ethical-ads", "sidebar-versions"] secondary_sidebar_items = ["page-toc", "edit-this-page", "sourcelink"] -footer_start = "scverse-footer" -footer_center = "" -footer_end = "" -show_toc_level = "2" -navigation_depth = "3" -collapse_navigation = "False" +footer_start = ["scverse-footer"] +footer_center = [] +footer_end = [] +show_toc_level = 2 +navigation_depth = 3 +collapse_navigation = false pygments_light_style = "tango" pygments_dark_style = "monokai" announcement = "https://scverse.org/announcement.html" From 99239cdf321bbf693af36fab62c69cb9ad007169 Mon Sep 17 00:00:00 2001 From: "Philipp A." Date: Thu, 22 Oct 2026 17:25:02 +0200 Subject: [PATCH 6/6] fix tests --- src/scverse_doc/theme/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scverse_doc/theme/__init__.py b/src/scverse_doc/theme/__init__.py index f41be5b..76b177d 100644 --- a/src/scverse_doc/theme/__init__.py +++ b/src/scverse_doc/theme/__init__.py @@ -72,11 +72,11 @@ def _accent(config: Config) -> str: def _secondary_accent(config: Config) -> str | None: accent: str | bool = config.html_theme_options.get("secondary_accent") - if not accent: - return None + if accent is not True: + return accent or None if pkg := packages.get(_package_name(config)): return pkg.secondary_accent - return str(accent) + return None def _add_chrome(config: Config) -> None: