Skip to content

Commit 02ce7d1

Browse files
authored
site: landing redesign + ancillary pages + brand footer + URL sweep (#1313)
* docs: sweep stale /en/latest/ URLs to /stable/; fix Yiqing rename The docs.suews.io site no longer serves at /en/latest/* (returns 404). Production now lives at /stable/<page>.html and dev at /latest/. Sweep all 13 occurrences across README, supy modules, and validation pipeline to use /stable/. Add a project doc-conventions rule so the pattern can be blocked in future PRs. Also fix the contributor entry: Yuanyuan Liu was a typo for Yiqing Liu (the actual contributor); rename in acknowledgement.rst. * site: add team, funding, dependencies pages with shared subpage.css Adds three editorial-register subpages to suews.io: - /team/ — masthead of 19 contributors grouped Lead / Major / Other, ordered per CITATION.cff with ORCID-linked names highlighted. Institutional attribution moved here from the old footer. - /funding/ — 23 grants in four sections (European, UK research councils, international, institutional) with D/O tagging. - /dependencies/ — six Fortran libraries with sources, purposes, and licence notes; pyproject.toml link for Python deps. site/css/subpage.css introduces neutrally-named .subpage-* / .entry-* classes shared across all three pages so future subpages can reuse without touching the team page's existing inline classes. * site(brand): adopt shared 4-link compact footer Brand pages still carried the old 2-link "Home / Brand Workshop" footer, which felt orphaned now that the rest of the site uses the compact Team / Funding / Dependencies / Brand nav. Sweep the new footer onto both brand pages for consistency. brand/index.html uses the shared .footer / .footer-nav classes from base.css. brand-workshop.html is self-contained (does not load base.css) so the footer is inline-styled to match the same compact aesthetic using the workshop's own --text-secondary / --text-muted / --border tokens. Side benefit: brand-workshop.html previously linked to the non-existent showcase.html — that broken link is gone. * site(landing): redesign with a11y, motion hygiene, two-tier resources Landing page rebuilt around the editorial-scientific register captured in the new .impeccable.md design context (also added to CLAUDE.md). Treats SUEWS as a quietly-confident scientific instrument rather than a SaaS product page. Resource cards are now in two tiers — Use SUEWS (Install / Documentation / Community) and Explore further (Source / Schema / UMEP) — each card carries a physics-mapped accent colour that ties back to the model's components (sun, water, vegetation, urban fabric). UMEP copy tightened to "provides a GUI for SUEWS" rather than the misleading "couples". Footer collapsed to two rows: a compact uppercase nav with dot separators (Team / Funding / Dependencies / Brand) and a single copyright line. Institutional attribution moved to /team/. Accessibility hygiene applied throughout: - Raised --text-muted contrast on both dark and light themes (was borderline for small text against the editorial background). - Added global :focus-visible outlines so keyboard navigation is visible on cards, links, and the theme toggle. - Added a prefers-reduced-motion block in base.css that suppresses the contour-ring pulse, wave undulation, sun glow, and staggered card reveals when the user has requested less motion. - Stopped the contour-ring pulse loop unconditionally (rings remain static — they're the signature, the motion was decorative competing with the wave + sun glow). - Removed bounce easing on the scroll indicator. - Removed decorative gradient stripes on resource cards and the double-gradient halo on the Applications box. Stats bar (15+ / 200+ / 20+) preserved in its absolute-positioned JetBrains Mono treatment — evidence over assertion is the rule for this register. * site(docs): refresh README for new pages, stylesheets, and footer site/README.md only documented index.html and pointed to a non-existent showcase.html. Rewrites to reflect the current shape: landing + team/funding/dependencies subpages, the three stylesheets (tokens / base / subpage), the shared compact footer, local-dev banner behaviour, and the auto-deploy trigger. * site(team): rename .team-* classes to shared .subpage-*/.entry-* The team page shipped with class names (team-header, team-person-*, team-group-*, etc.) that subpage.css does not define — the page was rendering with browser defaults for the contributor cards. Rename all 16 team-* class names to their subpage.css equivalents (subpage-header, subpage-group-*, subpage-list, entry, entry-name/subtitle/tag/detail/meta) so the masthead picks up the shared editorial layout used by /funding/ and /dependencies/. Pure HTML rename — no style or markup-structure changes; subpage.css itself is unchanged. * site(team): add ORCID links for five contributors Added ORCID links for Matthew Paskin, Silvia Rognone, Vitor Lavor, Keisuke Nakao, and Minttu P. Havu — verified by cross-checking each ORCID record's affiliation and publication topic against their SUEWS contribution area. Five remaining contributors (Zhenkun Li, Yihao Tang, Shiho Onomura, Thomas Loridan, Brian Offerle) have no public ORCID matching their SUEWS work and are left without a link. * site(team): add ORCID + current affiliation for Shiho Onomura Verified via expanded ORCID search (returns Tokyo City University) and her Google Scholar profile (tcu.ac.jp email, co-authored on the 2015 Onomura-Grimmond-Lindberg meteorological-forcing paper and the UMEP integrated-tool paper). Updated affiliation to reflect her move from Gothenburg to Tokyo City University. * site(team): link contributors without ORCID to Scholar/ResearchGate Three contributors have no public ORCID (verified by ORCID expanded search returning 0 hits and Crossref showing no ORCID attached to any of their papers): - Zhenkun Li -> ResearchGate (Shanghai Climate Centre) - Thomas Loridan -> Google Scholar (now at Reask, catastrophe modelling; previously King's College London) - Brian Offerle -> Google Scholar (now at FluxSense Sweden; previously Indiana / Gothenburg) Each link carries a title attribute naming the source so the target is clear to sighted users on hover and screen reader users on focus. * site(team): add contribution line for Xiaoxiong Xie Traced via git log: two direct commits (TMY radiation fix 2020-05-10, ERA5 pressure/RH units fix 2021-11-22) in src/supy/util/ plus four RSL/FAI-related issues opened between 2022 and 2025. Versions span v2020a through present. * site: refresh subpage titles, reorganise funding, rehouse brand under shared chrome - brand: rewrite to use shared .subpage-* chrome; drop SaaS-style cards/CTAs - team: shorten title, drop stale institutions, lede foregrounds the collaboration - funding: shorten title, reorder sections to match lede, swap project/funder columns so the grant is prominent, fix D/D·O alignment, add NE/P018637/1 code to the NERC IRF entry - footer: separator restyled; legal line now "SUEWS Team" - docs: acknowledgement.rst becomes a stub linking out to suews.io * docs(publications): add topic-based chip filter to publication pages Reads keyword tags from refs-SUEWS.bib / refs-community.bib and stamps each rendered bibliography <li> with pub-entry + pub-kw-<slug> classes via a new Sphinx extension. A client-side chip row above each bibliography lets readers filter the single flat list by topic (multi-select, union semantics) and syncs ?topic=<slug>[,<slug>] into the URL so external deep-links auto-apply a filter. - _ext/publications_topic_annotate.py: scoped citation_id -> key lookup via sphinxcontrib-bibtex's BibtexDomain, keyed by bibliography docname to avoid cross-page id collisions. - _static/publications-filter.{js,css}: chip rendering, multi-select, empty-state, URL sync, aria-pressed, prefers-reduced-motion. - related_publications.rst / community_publications.rst: .. raw:: html mount above the existing .. bibliography:: block; updated note to explain the filter and deep-link format. - conf.py: register the extension and load the JS/CSS site-wide. * site: redesign landing Applications as slug-based cards linked to filtered publications Replaces the flat 6-item dot-bullet list with six .resource-card entries matching the Resources section visually. Each card targets one bib keyword slug (energy-balance / water-balance / radiation / anthropogenic-heat / building-energy / carbon-flux) and deep-links to related_publications.html?topic=<slug>, which the docs chip filter reads on load. - Card chrome reuses existing .resource-card / .resource-grid / .section-header / .resource-tier-label, with accent tokens chosen to reflect the physical constituent of each topic (sky-blue for flux balance, water-blue for hydrology, sun-gold for radiation, etc.). - Adds .card-meta rule for the "N papers" caption in the same uppercase-tracked register as .resource-tier-label. - Removes obsolete .applications-box / .applications-grid / .app-item / .app-bullet rules and markup (~60 lines). - Local-dev IIFE extended: when running on localhost/127.0.0.1, rewrites the six Applications hrefs from docs.suews.io/stable/... to http://127.0.0.1:8000/... and prepends a gold LOCAL badge to each .card-meta so the routing override is visible.
1 parent 5a83374 commit 02ce7d1

File tree

27 files changed

+2481
-1026
lines changed

27 files changed

+2481
-1026
lines changed

.claude/rules/docs/conventions.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---
22
paths:
33
- docs/**/*
4+
- site/**/*
5+
- README.md
46
---
57

68
# Documentation Conventions
@@ -9,6 +11,20 @@ Conventions for SUEWS documentation (Sphinx, RST, Markdown).
911

1012
---
1113

14+
## Links to docs.suews.io — URL format
15+
16+
**Always** use the `/stable/` or `/latest/` prefix. **Never** use `/en/latest/`.
17+
18+
- Correct: `https://docs.suews.io/stable/acknowledgement.html`, `https://docs.suews.io/stable/getting-started.html`
19+
- Correct (dev version): `https://docs.suews.io/latest/acknowledgement.html`
20+
- Wrong (stale, 404s today): `https://docs.suews.io/en/latest/acknowledgement.html`
21+
22+
The `en/latest/` pattern is an old ReadTheDocs language-prefixed layout. The SUEWS docs site now serves at `/stable/` (production) and `/latest/` (development). `https://docs.suews.io/` itself 302-redirects to `/stable/`.
23+
24+
All known `/en/latest/` occurrences in the repo were swept to `/stable/` on 2026-04-19. If this pattern reappears in a PR, block it — it will 404 for users.
25+
26+
---
27+
1228
## Build Commands
1329

1430
```bash

.impeccable.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Impeccable Design Context — SUEWS
2+
3+
Captured via `/impeccable teach` on 2026-04-18.
4+
5+
This file is the single source of truth for design direction on any SUEWS-facing surface (suews.io landing, brand showcase, future dashboards, interactive documentation). All impeccable-family skills (`/craft`, `/polish`, `/critique`, `/animate`, etc.) read this file before producing work.
6+
7+
## Design Context
8+
9+
### Users
10+
11+
Two audiences, weighted **equally**:
12+
13+
- **Urban climate researchers & PhD students** — landing on suews.io to evaluate whether SUEWS fits their science question, verify citations and provenance, reach the Sphinx docs, or find the Discourse community. Technical, sceptical, will notice unsupported claims.
14+
- **Architects, engineers, and built-environment consultancies** (exemplified by the Foster + Partners / Arup lines of enquiry) — assessing whether SUEWS is serious enough to use in commercial microclimate workflows. Not a pure software-buyer audience; they want proof of scientific rigour, not a SaaS pitch.
15+
16+
Secondary, design around but do not optimise for: UMEP/QGIS users arriving sideways, funders skimming for institutional credibility, and journalists chasing urban-heat stories.
17+
18+
Job-to-be-done on suews.io: **"Convince me in 30 seconds this is a serious, well-maintained, scientifically grounded model — then get me to the docs."**
19+
20+
### Brand Personality
21+
22+
Ting's words: **quietly confident, not fancy.**
23+
24+
Expanded: *rigorous, grounded, unshowy, enduring.* SUEWS has run for 15+ years across 20+ cities — the design should feel like a well-kept scientific instrument, not a 2025 AI startup. No hype, no glow, no "revolutionary". Authority comes from restraint and from the evidence on the page (citations, years active, institutional backing).
25+
26+
Emotional goals: **trust, competence, calm.** A reader should feel they've landed somewhere careful.
27+
28+
### Aesthetic Direction
29+
30+
The existing visual system in `site/` is the established brand. Treat it as canonical, not a starting point to redesign:
31+
32+
- **Palette**: Deep Blue `#2D3142`, Warm Stone `#5D5852`, Golden Sun `#F7B538`, Sun Core `#E85D04`, Forest Green `#09a25c`, Ocean Blue `#0077B6`, Sky Blue `#5DADE2`, Wave Blue `#0558a5`. These map to the physical constituents of the model (urban fabric, sun, vegetation, water) — the palette is the model's conceptual diagram.
33+
- **Theme**: dark default (suits the editorial/scientific register and the audience's typical viewing context — late-evening reading, researchers in dim offices) with a working light toggle. Respect `prefers-color-scheme`.
34+
- **Typography** — intentionally kept: Crimson Pro (display/serif), Instrument Sans (UI/body), JetBrains Mono (code/metrics). Yes, Crimson Pro and Instrument Sans appear on impeccable's "reflex fonts" watch-list, but they are already load-bearing on suews.io and the brand has settled. Do **not** churn them on the landing page. For *new* surfaces (dashboards, interactive tools, posters) where there's no prior commitment, reach beyond the reflex list first.
35+
- **Signature motifs**: topographic contour rings as ambient background, golden-hour halo on the logo, golden-ratio composition (1:1.618) for the logo glyph. These are the one-thing-you-remember — keep them.
36+
- **Register**: editorial-scientific, closer to a Royal Society journal landing page than a software product page. Facts in roman; emphasis via italic and weight, not colour shifts or gradients.
37+
38+
### Anti-references
39+
40+
SUEWS should explicitly *not* look like:
41+
42+
- A generic AI/ML startup (cyan-on-dark, purple-to-blue gradients, glowing neon accents, glassmorphism cards).
43+
- A 2024–2025 AI-generated template (identical cards in a 3-col grid, rounded-rect icon above every heading, gradient text for impact).
44+
- A consumer SaaS marketing page (big-number hero metrics in service of conversion, aggressive CTAs, persuasion patterns).
45+
- A legacy university-department site (1990s table layouts, Comic Sans, stacked centred everything).
46+
47+
### Accessibility
48+
49+
Target: **WCAG 2.2 AA across the whole site**. AAA is not a design goal — it would force the palette into a flatness that fights the editorial register, and the audience doesn't warrant it.
50+
51+
Required practices on every new surface:
52+
53+
- Text contrast verified against actual palette combinations (the current `text-muted` token `rgba(255,255,255,0.35)` on `#0F1119` is borderline for small text — audit before reuse).
54+
- Honour `prefers-reduced-motion`. The landing page currently has looping contour pulse, wave undulation, sun glow, and staggered card reveals — all of these must be reduced to static or single-shot when the user has requested less motion.
55+
- Focus states visible on all interactive surfaces. The current theme toggle and resource cards must have keyboard-visible focus rings, not only hover states.
56+
- No colour-only meaning. The golden-sun / water-blue / forest-green mapping to physical constituents is evocative but must not be the sole carrier of information.
57+
- No synthetic bold on display type; ensure Crimson Pro ships the real weights.
58+
59+
### Design Principles
60+
61+
Use these as the tie-breaker whenever a design decision is contested. If you can't tell whether a change improves the site, ask which principle it serves.
62+
63+
1. **Evidence over assertion.** Prefer showing (citations, years, sites, institutional names, a real log of activity) over claiming ("powerful", "industry-leading"). If a claim can't be backed on the page, cut it.
64+
2. **Restraint is the voice.** Quiet confidence means one accent colour at a time, one motion at a time, whitespace over decoration. If a flourish doesn't earn its place, delete it.
65+
3. **The palette is the model.** Sun / water / vegetation / urban fabric colours map to what SUEWS simulates — use them to reinforce meaning, not as decoration. Don't introduce colours from outside this system.
66+
4. **Readable first, elegant second.** Body type at a comfortable size, line length capped at ~70ch, contrast audited. An elegant layout that fatigues a researcher after two screens is a failure.
67+
5. **Keep the topographic signature.** Contour rings, golden-hour glow, golden-ratio composition — these are what distinguish SUEWS from every other academic-software landing. Protect them; don't dilute them with generic decoration.

CLAUDE.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,36 @@ Full setup: `/setup-dev` | Style check: `/lint-code` | Build check: `/verify-bui
4242

4343
Rules in `.claude/rules/` load automatically based on files being edited.
4444

45+
## Design Context
46+
47+
Applies to any SUEWS-facing surface: `site/` landing, brand showcase, future dashboards, interactive docs. Full rationale and tie-breaker principles live in `.impeccable.md` at project root — read it before any design work (`/craft`, `/polish`, `/critique`, `/animate`, etc.).
48+
49+
- **Users** — Two audiences, equally weighted:
50+
- Urban climate researchers & PhD students verifying SUEWS is scientifically sound.
51+
- Architects, engineers, and built-environment consultancies (Foster + Partners, Arup pattern) assessing commercial fit.
52+
- Job-to-be-done: "Convince me in 30 seconds this is a serious, well-maintained, scientifically grounded model — then get me to the docs."
53+
- **Brand personality***Quietly confident, not fancy.* Rigorous, grounded, unshowy, enduring. Emotional goals: trust, competence, calm. No hype, no glow, no "revolutionary".
54+
- **Aesthetic direction** — Established visual system in `site/` is canonical:
55+
- Palette tied to the model's physics (Deep Blue `#2D3142`, Golden Sun `#F7B538`, Sun Core `#E85D04`, Forest Green `#09a25c`, Ocean Blue `#0077B6`, Sky Blue `#5DADE2`, Wave Blue `#0558a5`).
56+
- Dark default with working light toggle; honour `prefers-color-scheme`.
57+
- Typography kept: Crimson Pro (display), Instrument Sans (UI), JetBrains Mono (code). Load-bearing on the landing — do not churn. For *new* surfaces, reach beyond these first.
58+
- Signature motifs: topographic contour rings, golden-hour halo, golden-ratio logo composition. Protect; do not dilute.
59+
- **Anti-references** — Do NOT look like a generic AI/ML startup (cyan-on-dark, purple gradients, glassmorphism), a 2024–2025 AI template (identical card grids, gradient text), a consumer SaaS page, or a legacy university department site.
60+
- **Accessibility** — Target WCAG 2.2 AA (not AAA — fights the editorial register). Required on every surface:
61+
- Contrast audited against the actual palette (the `text-muted` token is borderline for small text).
62+
- Honour `prefers-reduced-motion` — the landing page has several concurrent animations.
63+
- Visible keyboard focus rings everywhere.
64+
- No colour-only meaning; ensure the Crimson Pro weights are real, not synthetic bold.
65+
- **Design principles** (tie-breakers when a change is contested):
66+
1. Evidence over assertion.
67+
2. Restraint is the voice.
68+
3. The palette is the model.
69+
4. Readable first, elegant second.
70+
5. Keep the topographic signature.
71+
4572
## References
4673

74+
- `.impeccable.md` - Design context (full rationale, used by impeccable-family skills)
4775
- `.claude/README.md` - Full workspace documentation
4876
- `.claude/skills/` - Detailed skill workflows
4977
- `.claude/rules/` - Style conventions

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ Full documentation: **[docs.suews.io](https://docs.suews.io)**
6464

6565
## Documentation
6666

67-
* **[Getting Started](https://docs.suews.io/en/latest/getting-started.html)** -- installation and first simulation
68-
* **[Tutorials](https://docs.suews.io/en/latest/tutorials.html)** -- hands-on guides with sample data
69-
* **[Input/Output Reference](https://docs.suews.io/en/latest/inputs.html)** -- configuration and data formats
70-
* **[Scientific Background](https://docs.suews.io/en/latest/parameterisations-and-sub-models.html)** -- physics schemes and parameterisations
67+
* **[Getting Started](https://docs.suews.io/stable/workflow.html)** -- installation and first simulation
68+
* **[Tutorials](https://docs.suews.io/stable/auto_examples/index.html)** -- hands-on guides with sample data
69+
* **[Input/Output Reference](https://docs.suews.io/stable/inputs/index.html)** -- configuration and data formats
70+
* **[Scientific Background](https://docs.suews.io/stable/parameterisations-and-sub-models.html)** -- physics schemes and parameterisations
7171
* **[Community Forum](https://community.suews.io)** -- questions, discussion, and support
7272

7373
## Citation

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ This directory contains the complete documentation build system for SUEWS (Surfa
2121
- **`installation.rst`**: Installation instructions
2222
- **`workflow.rst`**: User workflow guidance
2323
- **`notation.rst`**: Mathematical notation and symbols
24-
- **`acknowledgement.rst`**: Credits and acknowledgments
24+
- **`acknowledgement.rst`**: Stub page linking to canonical team / funding / dependencies pages on suews.io
2525
- **`troubleshooting.rst`**: Common issues and solutions
2626

2727
#### Input/Output Reference
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""
2+
Sphinx extension: publications_topic_annotate.
3+
4+
Tags each bibliography ``<li>`` on the two publication pages with CSS
5+
classes derived from the BibTeX entry's ``keywords`` field so the
6+
client-side chip filter (``publications-filter.js``) can hide/show
7+
entries by topic without duplicating them across static subsections.
8+
9+
Pipeline:
10+
11+
* ``builder-inited``: parse ``refs-SUEWS.bib`` and ``refs-community.bib``
12+
once with pybtex and cache a ``citekey -> [slug, ...]`` map on
13+
``app.env``.
14+
* ``doctree-resolved``: on the two publication pages only, read the
15+
sphinxcontrib-bibtex domain's ``citations`` list to get the mapping
16+
from rendered ``citation_id`` (the docutils-generated ``id2`` etc.
17+
on the ``<li>``) back to the original bib citekey. Walk every
18+
``list_item`` node and, when its first id matches a known citation,
19+
extend ``classes`` with ``pub-entry`` plus one ``pub-kw-<slug>`` per
20+
keyword slug. Docutils turns those into ``class=`` on the rendered
21+
``<li>`` element.
22+
23+
The bib file read via pybtex is the source of truth for keywords;
24+
sphinxcontrib-bibtex is consulted only to bridge rendered ids back to
25+
cite keys.
26+
"""
27+
28+
from __future__ import annotations
29+
30+
from pathlib import Path
31+
32+
from docutils import nodes
33+
from pybtex.database import parse_file
34+
35+
TOPIC_PAGES = frozenset({"related_publications", "community_publications"})
36+
BIB_FILES = ("assets/refs/refs-SUEWS.bib", "assets/refs/refs-community.bib")
37+
38+
39+
def _parse_bibs(srcdir: Path) -> dict[str, list[str]]:
40+
"""Return ``{citekey: [slug, ...]}`` for both SUEWS bib files."""
41+
out: dict[str, list[str]] = {}
42+
for bib_path in BIB_FILES:
43+
full = Path(srcdir) / bib_path
44+
if not full.exists():
45+
continue
46+
db = parse_file(str(full), bib_format="bibtex")
47+
for key, entry in db.entries.items():
48+
raw = entry.fields.get("keywords", "")
49+
slugs = sorted({s.strip() for s in raw.split(",") if s.strip()})
50+
out[key] = slugs
51+
return out
52+
53+
54+
def _citation_id_to_key(app, docname: str) -> dict[str, str]:
55+
"""Map rendered docutils ids on bibliography ``<li>`` back to cite keys.
56+
57+
Scoped to ``docname`` because sphinxcontrib-bibtex reuses short ids
58+
(``id2``, ``id3``, ...) inside each document, so a global map causes
59+
cross-page collisions.
60+
"""
61+
try:
62+
domain = app.env.get_domain("cite")
63+
except Exception:
64+
return {}
65+
citations = getattr(domain, "citations", []) or []
66+
return {
67+
c.citation_id: c.key
68+
for c in citations
69+
if c.bibliography_key.docname == docname
70+
}
71+
72+
73+
def _on_builder_inited(app) -> None:
74+
app.env.pub_topic_map = _parse_bibs(Path(app.srcdir))
75+
76+
77+
def _on_doctree_resolved(app, doctree, docname: str) -> None:
78+
if docname not in TOPIC_PAGES:
79+
return
80+
citekey_map: dict[str, list[str]] = getattr(app.env, "pub_topic_map", {}) or {}
81+
if not citekey_map:
82+
return
83+
id_to_key = _citation_id_to_key(app, docname)
84+
if not id_to_key:
85+
return
86+
for list_item in doctree.traverse(nodes.list_item):
87+
ids = list_item.get("ids") or []
88+
citekey = next((id_to_key[i] for i in ids if i in id_to_key), None)
89+
if citekey is None:
90+
continue
91+
slugs = citekey_map.get(citekey, [])
92+
to_add = ["pub-entry"] + [f"pub-kw-{s}" for s in slugs]
93+
existing = list_item.get("classes") or []
94+
for cls in to_add:
95+
if cls not in existing:
96+
existing.append(cls)
97+
list_item["classes"] = existing
98+
99+
100+
def setup(app):
101+
app.connect("builder-inited", _on_builder_inited)
102+
app.connect("doctree-resolved", _on_doctree_resolved)
103+
return {
104+
"version": "0.1",
105+
"parallel_read_safe": True,
106+
"parallel_write_safe": True,
107+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* publications-filter.css — chip styling for the topic filter on
3+
* `related_publications` and `community_publications`.
4+
*
5+
* Uses pydata_sphinx_theme / sphinx_book_theme CSS custom properties
6+
* (`--pst-color-*`) so the chips feel native to the docs theme in both
7+
* light and dark modes, with sensible fallbacks.
8+
*/
9+
10+
.pub-filter {
11+
display: flex;
12+
flex-wrap: wrap;
13+
gap: 8px;
14+
margin: 16px 0 28px;
15+
padding: 0;
16+
}
17+
18+
.pub-filter__chip {
19+
display: inline-flex;
20+
align-items: center;
21+
gap: 8px;
22+
padding: 6px 14px;
23+
font: inherit;
24+
font-size: 0.85rem;
25+
line-height: 1.4;
26+
color: var(--pst-color-text-base, #374151);
27+
background: transparent;
28+
border: 1px solid var(--pst-color-border, rgba(0, 0, 0, 0.15));
29+
border-radius: 999px;
30+
cursor: pointer;
31+
transition: background-color 200ms ease, border-color 200ms ease, color 200ms ease;
32+
white-space: nowrap;
33+
}
34+
35+
.pub-filter__chip:hover:not(:disabled) {
36+
border-color: var(--pst-color-primary, #0558a5);
37+
color: var(--pst-color-primary, #0558a5);
38+
}
39+
40+
.pub-filter__chip:focus-visible {
41+
outline: 2px solid var(--pst-color-primary, #0558a5);
42+
outline-offset: 2px;
43+
}
44+
45+
.pub-filter__chip.is-active {
46+
background: var(--pst-color-primary, #0558a5);
47+
border-color: var(--pst-color-primary, #0558a5);
48+
color: var(--pst-color-background, #ffffff);
49+
}
50+
51+
.pub-filter__chip.is-empty,
52+
.pub-filter__chip:disabled {
53+
opacity: 0.45;
54+
cursor: not-allowed;
55+
}
56+
57+
.pub-filter__chip--all {
58+
font-weight: 500;
59+
}
60+
61+
.pub-filter__count {
62+
font-size: 0.75rem;
63+
opacity: 0.75;
64+
font-variant-numeric: tabular-nums;
65+
}
66+
67+
.pub-filter__empty {
68+
margin: 12px 0 24px;
69+
font-style: italic;
70+
color: var(--pst-color-text-muted, #6b7280);
71+
}
72+
73+
@media (prefers-reduced-motion: reduce) {
74+
.pub-filter__chip {
75+
transition: none;
76+
}
77+
}

0 commit comments

Comments
 (0)