Theme refactor: shared base.css + palette-only themes (minified); template P4/P5/P7/P11 skip-link; per-image alt; system theme default

This commit is contained in:
Richard Zink
2026-09-10 08:51:59 +02:00
parent 3a100364aa
commit 212a98e7f2
10 changed files with 619 additions and 2364 deletions
+28 -4
View File
@@ -34,7 +34,15 @@
if (subRoot.note.hasRelation("shareOpenGraphImage")) {
ogImageRel = `api/images/${subRoot.note.getRelation("shareOpenGraphImage").value}/image.png`;
}
const ogImage = ogImageRel ? rootUrl + ogImageRel : "";
// Per-article OG image: a thumbnail child note beats the site-wide fallback.
if (note.getLabelValue("publish") === "true") {
try {
const thumbs = (note.getChildNotes() || [])
.filter(n => /^(?:thumb|.+_thumb)\.[A-Za-z0-9]+$/i.test(n.title));
if (thumbs.length) ogImageRel = `api/notes/${thumbs[0].noteId}/download`;
} catch (e) {}
}
const ogImage = ogImageRel ? (ogImageRel.startsWith("http") ? ogImageRel : rootUrl + ogImageRel) : "";
const jsonLd = [];
if (note.noteId === subRoot.note.noteId) {
@@ -71,8 +79,9 @@
<title><%= pageTitle %></title>
<% if (pageUrl) { %><link rel="canonical" href="<%= pageUrl %>"><% } %>
<script>
// Site theme toggle (light/dark); default is dark.
const theme = localStorage.getItem("theme") || "dark";
// Site theme toggle: explicit choice wins, else follow OS, else dark.
const theme = localStorage.getItem("theme")
|| (window.matchMedia && matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark");
document.documentElement.classList.add(`theme-${theme}`);
window.glob = {
@@ -127,6 +136,7 @@ content = content.replaceAll(headingRe, (...match) => {
});
%>
<body data-note-id="<%= note.noteId %>" class="type-<%= note.type %>" data-ancestor-note-id="<%= subRoot.note.noteId %>">
<a href="#content" class="skip-link">Skip to content</a>
<%- renderSnippets("body:start") %>
<div id="header">
<button aria-label="<%= t("share_theme.toggle-navigation") %>" id="left-pane-toggle-button" class="header-button"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v2H4zm0 5h16v2H4zm0 5h16v2H4z"></path></svg></button>
@@ -180,7 +190,21 @@ content = content.replaceAll(headingRe, (...match) => {
<p><%= t("share_page.no-content") %></p>
<% } else { %>
<%
content = content.replace(/<img /g, `<img alt="${t("share_theme.image_alt")}" loading="lazy" `);
const pageTitleEsc = note.title.replace(/"/g, "&quot;");
content = content.replace(/<img\b([^>]*)>/g, (m, attrs) => {
let a = attrs;
if (!/\bloading=/i.test(a)) a += ' loading="lazy"';
if (!/\bdecoding=/i.test(a)) a += ' decoding="async"';
if (!/\balt=/i.test(a)) {
let alt = pageTitleEsc;
const srcm = a.match(/src="[^"]*\/([^"?]+)"/);
if (srcm && !/\/download$/i.test(srcm[1])) {
alt = decodeURIComponent(srcm[1]).replace(/[-_]+/g, " ").replace(/\.[A-Za-z0-9]+$/, "").trim() || alt;
}
a += ` alt="${alt}"`;
}
return "<img" + a + ">";
});
%>
<%- content %>
<% } %>