From 9814741b54c4333f7526390efb3f03e40c5de961 Mon Sep 17 00:00:00 2001 From: Lukas Wallrich Date: Mon, 9 Nov 2026 17:53:17 +0000 Subject: [PATCH 1/5] Render italics and small caps in publication titles Publication titles from Crossref carry , and markup and HTML entities, which the cluster cards showed as literal text. A new partial, clusters/rich_text.html, escapes the title and restores only those tags (scp as small caps). featured-resources.js does the same for the reference popup and the reading list, and copies and exports plain text. The popup and reading-list APA references join the D15 italics exception for reference lists. Co-Authored-By: Claude Opus 5.5 --- assets/css/doors/base.css | 5 ++- .../partials/clusters/cluster_section.html | 4 +-- layouts/partials/clusters/featured_cards.html | 4 +-- layouts/partials/clusters/rich_text.html | 7 ++++ static/js/featured-resources.js | 36 ++++++++++++++----- 5 files changed, 43 insertions(+), 13 deletions(-) create mode 100644 layouts/partials/clusters/rich_text.html diff --git a/assets/css/doors/base.css b/assets/css/doors/base.css index 4defd74672b..36175115303 100644 --- a/assets/css/doors/base.css +++ b/assets/css/doors/base.css @@ -19,10 +19,13 @@ main:lang(zh){--display:'Noto Serif CJK SC','Songti SC','STSong',SimSun,serif;-- *,*::before,*::after,.font-italic{font-style:normal!important} em,i:not([class]),dfn,cite,var{font-weight:600} .references-list :is(em,i),.references-page .article-style :is(em,i), -:is(h2,h3,h4)[id^=references i]~:is(ul,ol,p) :is(em,i),.csl-bib-body :is(em,i){font-style:italic!important;font-weight:400} +:is(h2,h3,h4)[id^=references i]~:is(ul,ol,p) :is(em,i),.csl-bib-body :is(em,i), +.ref-apa :is(em,i),.rl-detail-apa :is(em,i){font-style:italic!important;font-weight:400} .wrap{width:min(1280px,calc(100% - 96px));margin-inline:auto} .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap} +/* Small capitals in publication titles from Crossref (), see clusters/rich_text.html */ +.scp{font-variant:small-caps} .skip{position:fixed;top:-100px;inset-inline-start:12px;z-index:1100;background:var(--ink);color:#fff;padding:12px 16px} .skip:focus,.skip:hover{top:8px;color:#fff} .crumbs{font-size:15px;line-height:1.4;color:var(--ink)} diff --git a/layouts/partials/clusters/cluster_section.html b/layouts/partials/clusters/cluster_section.html index 8fae4c85ebc..232b5678be9 100644 --- a/layouts/partials/clusters/cluster_section.html +++ b/layouts/partials/clusters/cluster_section.html @@ -91,7 +91,7 @@

'; html += '
'; - html += '
' + escHtml(r.title || doi) + '
'; + html += '
' + (r.title ? richText(r.title) : escHtml(doi)) + '
'; html += '
' + escHtml(r.short_ref || '') + '
'; html += '
'; html += ''; @@ -268,7 +288,7 @@ html += '
'; if (r.apa) { html += '
'; - html += '
' + escHtml(r.apa) + '
'; + html += '
' + richText(r.apa) + '
'; html += ''; @@ -365,7 +385,7 @@ var d = copyBtn.getAttribute('data-copy-apa'); var res = resourceIdx[d]; if (res && res.apa) { - navigator.clipboard.writeText(res.apa).then(function () { + navigator.clipboard.writeText(plainText(res.apa)).then(function () { copyBtn.classList.add('copied'); setTimeout(function () { copyBtn.classList.remove('copied'); }, 1500); }); @@ -467,7 +487,7 @@ doc.setFont('helvetica', 'bold'); doc.setFontSize(11); doc.setTextColor(34, 34, 34); - var titleLines = doc.splitTextToSize(r.title || doi, contentW); + var titleLines = doc.splitTextToSize(r.title ? plainText(r.title) : doi, contentW); doc.text(titleLines, margin, y); y += titleLines.length * 5 + 2; @@ -477,7 +497,7 @@ doc.setFont('times', 'normal'); doc.setFontSize(9); doc.setTextColor(68, 68, 68); - var apaLines = doc.splitTextToSize(r.apa, contentW - 10); + var apaLines = doc.splitTextToSize(plainText(r.apa), contentW - 10); // Hanging indent: first line at margin, rest indented apaLines.forEach(function (line, li) { checkPage(4); From c0191757f6f0e7b249a55291cdcdf8c40c11ddf5 Mon Sep 17 00:00:00 2001 From: Lukas Wallrich Date: Mon, 9 Nov 2026 17:59:27 +0000 Subject: [PATCH 2/5] Show italics in publication titles and check cluster pages for literal markup Card, popup and reading-list titles join the D15 exception for reference lists, so journal names and species set in are italic. check_seo_html.py check 17 fails a build whose cluster pages show <i>, <em> or <scp> as text or leave unrendered. Co-Authored-By: Claude Opus 5.5 --- assets/css/doors/base.css | 2 ++ scripts/check_seo_html.py | 16 ++++++++++++++++ scripts/tests/test_check_seo_html.py | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/assets/css/doors/base.css b/assets/css/doors/base.css index 36175115303..cfbe2714faf 100644 --- a/assets/css/doors/base.css +++ b/assets/css/doors/base.css @@ -21,6 +21,8 @@ em,i:not([class]),dfn,cite,var{font-weight:600} .references-list :is(em,i),.references-page .article-style :is(em,i), :is(h2,h3,h4)[id^=references i]~:is(ul,ol,p) :is(em,i),.csl-bib-body :is(em,i), .ref-apa :is(em,i),.rl-detail-apa :is(em,i){font-style:italic!important;font-weight:400} +/* Publication titles from those lists keep their journal names and species in italics, at the title's weight. */ +:is(.fr-title,.fc-title,.ref-popup-title,.rl-panel-item-title) i{font-style:italic!important;font-weight:inherit} .wrap{width:min(1280px,calc(100% - 96px));margin-inline:auto} .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap} diff --git a/scripts/check_seo_html.py b/scripts/check_seo_html.py index 41ce3f7d0cb..0d3ea51153c 100644 --- a/scripts/check_seo_html.py +++ b/scripts/check_seo_html.py @@ -309,6 +309,21 @@ def check_img_alt(pages): for p in pages if p.imgs_without_alt] +LITERAL_TITLE_MARKUP = re.compile(r"</?(?:i|em|scp)>|", re.IGNORECASE) + + +def check_cluster_title_markup(public_dir): + """Cluster pages render publication-title markup (clusters/rich_text.html), never show it.""" + found = [] + for root, _dirs, files in os.walk(os.path.join(public_dir, "clusters")): + if "index.html" in files: + full = os.path.join(root, "index.html") + with open(full, encoding="utf-8", errors="replace") as handle: + if LITERAL_TITLE_MARKUP.search(handle.read()): + found.append(os.path.relpath(full, public_dir)) + return sorted(found) + + def check_citation_targets(pages): return ["%s (%s)" % (p.path, href or "missing href") for p in pages for href in p.citations @@ -564,6 +579,7 @@ def main(public_dir): "14. no inline