From dd766eb976694928fded9cd7187df26b1b20a5c3 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 13 Sep 2026 15:57:19 -0400 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20the=20favicon=20and=20nav=20brand=20?= =?UTF-8?q?mark=20rendered=20blank=20=E2=80=94=20"--"=20inside=20an=20XML?= =?UTF-8?q?=20comment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 7ca6ee0's favicon.svg had a comment naming the `--fc-chrome-rgb` custom property. `--` is illegal inside an XML comment, so the file is not well-formed XML, and a browser renders an SVG-as-image only if it parses. It fails without any error, just a blank image. Because TopNav's brand glyph is the same file, the tab icon and the nav mark both went missing on the #251 deploy. logo.svg parses and was unaffected. The comment now names the property without the hyphens, and says why it has to. tests/test_public_svgs.py parses every SVG under frontend/public. It includes a vacuity guard (the directory really contains the two known files) and a positive control (the exact defect raises ParseError), so the check can actually fail. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SHQB1YukL3VyvMK8rcbmV9 --- frontend/public/favicon.svg | 5 ++++- tests/test_public_svgs.py | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/test_public_svgs.py diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg index 9cb1c69..d2d1914 100644 --- a/frontend/public/favicon.svg +++ b/frontend/public/favicon.svg @@ -15,7 +15,10 @@ Colours are theme tokens (frontend/src/theme/fabled-tokens.js): obsidian plate, accent gold. The plate is kept here (unlike logo.svg) so the tab icon is self-contained against any browser chrome; on the nav it is - invisible because it matches --fc-chrome-rgb exactly. --> + invisible because it matches the fc-chrome-rgb custom property exactly. + No double hyphen may appear inside this comment: XML forbids it, and a + browser refuses to render an SVG that does not parse (it happened once — + tests/test_public_svgs.py). --> diff --git a/tests/test_public_svgs.py b/tests/test_public_svgs.py new file mode 100644 index 0000000..7e1ff87 --- /dev/null +++ b/tests/test_public_svgs.py @@ -0,0 +1,40 @@ +"""Every SVG the browser loads from `frontend/public/` must parse as XML. + +A browser renders an SVG used as an image only if it is well-formed XML, and +when it is not, nothing reports it: no console error in most browsers, no +failed build, no failed request — the icon is simply blank. `favicon.svg` +shipped that way (merge #251) because its comment contained a CSS custom +property name, and `--` is illegal inside an XML comment. Both the tab icon and +the nav brand mark went missing, and only a person looking at the page noticed. +""" +from __future__ import annotations + +import xml.etree.ElementTree as ET +from pathlib import Path + +import pytest + +PUBLIC = Path(__file__).resolve().parent.parent / "frontend" / "public" +SVGS = sorted(PUBLIC.rglob("*.svg")) + + +def test_the_public_dir_has_svgs_to_check(): + """Guards the guard: a moved directory would otherwise pass vacuously.""" + assert {p.name for p in SVGS} >= {"favicon.svg", "logo.svg"} + + +@pytest.mark.parametrize("svg", SVGS, ids=lambda p: p.name) +def test_svg_is_well_formed_xml(svg): + root = ET.parse(svg).getroot() + assert root.tag == "{http://www.w3.org/2000/svg}svg" + + +def test_the_parser_rejects_the_shape_that_broke_the_favicon(): + """Positive control: the exact defect must fail this parser, or the + parametrized test above proves nothing.""" + broken = ( + '' + "" + ) + with pytest.raises(ET.ParseError): + ET.fromstring(broken) -- 2.54.0 From 57c880a623892d4fca077262e831b1dd66cb1a4e Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 13 Sep 2026 15:58:40 -0400 Subject: [PATCH 2/4] feat: the full brand mark as a faint backdrop behind every page The operator asked for one large version of the logo as the site background. It is pinned to the viewport behind .fc-content at about 6% strength, so it shows in the gutters and on bare page ground while cards and the nav cover it. The series reader is immersive, skips the shell, and never draws over it. It is a layered background (the page colour at 94% over logo.svg), not an overlay element with opacity. An overlay needs the content z-indexed above it, which turns every page into one stacking context under the nav's z-index 1000 and can trap an in-page overlay beneath the nav. A background changes nothing about stacking. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SHQB1YukL3VyvMK8rcbmV9 --- frontend/src/components/AppShell.vue | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/frontend/src/components/AppShell.vue b/frontend/src/components/AppShell.vue index 2123194..24022f6 100644 --- a/frontend/src/components/AppShell.vue +++ b/frontend/src/components/AppShell.vue @@ -17,6 +17,22 @@ const route = useRoute()