Email dark-mode skin; tree title links to tree, hostname to site root

- Add @media (prefers-color-scheme: dark) rules that re-tint every
  surface, border, and text color via case-insensitive
  attribute-substring selectors on style="...". Palette mirrors
  BockenTheme dark mode so the email reads as one product with
  the website on clients that honour prefers-color-scheme
  (Apple Mail / iOS Mail / Outlook for Mac and iOS, Gmail web).
- Add color-scheme + supported-color-schemes meta tags.
- Masthead H1 (tree title) now links to TreePage; the hostname
  line below now links to HomePage (site root) and displays the
  bare hostname without the /tree/<name> suffix.
This commit is contained in:
2026-05-15 16:05:16 +02:00
parent 90ad060421
commit 7402843d07
+72 -9
View File
@@ -5,6 +5,7 @@ declare(strict_types=1);
use Fisharebest\Webtrees\Date;
use Fisharebest\Webtrees\Fact;
use Fisharebest\Webtrees\Family;
use Fisharebest\Webtrees\Http\RequestHandlers\HomePage;
use Fisharebest\Webtrees\Http\RequestHandlers\TreePage;
use Fisharebest\Webtrees\I18N;
use Fisharebest\Webtrees\Individual;
@@ -569,12 +570,70 @@ $timeline_arrow_row = '<tr>'
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<meta name="supported-color-schemes" content="light dark">
<title><?= e(I18N::translate('Family newsletter — %s', $masthead_date($generated_at))) ?></title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;1,300;1,400&display=swap');
body { margin: 0; padding: 0; background: <?= $palette['bg'] ?>; }
a:hover { color: <?= $palette['link_hov'] ?> !important; }
.nl-tr:last-child { border-bottom: 0 !important; }
/*
* Dark-mode skin — mirrors the BockenTheme dark palette so the
* newsletter reads as one product with the website on every
* device that honours prefers-color-scheme (Apple Mail / iOS
* Mail / Outlook for Mac and iOS, Gmail web with a Gmail account).
*
* The inline styles in the template all reference fixed hex
* values from the light palette, so we lean on case-insensitive
* attribute-substring selectors to re-tint them without having
* to thread classes through every span and td. !important is
* required to beat the inline declarations.
*/
@media (prefers-color-scheme: dark) {
body { background: #0d0d0d !important; color: #e5e5e5 !important; }
a:hover { color: #8FBCBB !important; }
/* Surfaces */
[style*="background:#f8f6f1" i] { background-color: #0d0d0d !important; }
[style*="background:#efecea" i] { background-color: #1a1a1a !important; }
[style*="background:#dfdcd8" i] { background-color: #222 !important; }
/* Hairline + accent borders */
[style*="border:1px solid #ddd" i],
[style*="border-top:1px solid #ddd" i],
[style*="border-bottom:1px solid #ddd" i] { border-color: #2a2a2a !important; }
[style*="border-top:1px dashed #ddd" i] { border-top-color: #2a2a2a !important; }
/* Timeline rail — muted grey on dark */
[style*="border-left:4px solid #cdc7be" i] { border-left-color: #444 !important; }
/* The chevron arrow SVG carries stroke="#cdc7be" attribute; the
CSS attribute-substring trick on the surrounding span styles
doesn't reach into the SVG, so we restyle via stroke. */
svg[stroke="#cdc7be"] { stroke: #444 !important; }
/* Text colours — ink, ink2, ink3, mute */
[style*="color:#2a2a2a" i] { color: #e5e5e5 !important; }
[style*="color:#555" i] { color: #b6b6b6 !important; }
[style*="color:#777" i] { color: #8a8a8a !important; }
[style*="color:#aaa" i] { color: #666 !important; }
/* Links and the matching birth/dot accent (same hex) */
[style*="color:#5E81AC" i] { color: #88C0D0 !important; }
[style*="background:#5E81AC" i] { background-color: #88C0D0 !important; }
[style*="border-bottom:1px solid #5E81AC" i],
[style*="border-bottom:1px solid #5E81AC33" i] { border-bottom-color: #88C0D0 !important; }
/* Event-type icons — death (graphite) lifts; marriage (lavender)
stays since it already reads on dark. */
[style*="color:#4C566A" i] { color: #B0B6BF !important; }
/* The intro red rule keeps its accent — Nord red reads on
dark just as it does on cream — but we re-declare so the
attribute selector wins explicitly over inheritance. */
[style*="border-left:3px solid #BF616A" i] { border-left-color: #BF616A !important; }
}
</style>
</head>
<body style="margin:0;padding:0;background:<?= $palette['bg'] ?>;color:<?= $palette['ink'] ?>;">
@@ -593,20 +652,24 @@ $timeline_arrow_row = '<tr>'
<div style="font-size:11px;font-weight:600;letter-spacing:0.22em;text-transform:uppercase;color:<?= $palette['link'] ?>;">
<?= e(I18N::translate('Family Chronicle')) ?>
</div>
<h1 style="margin:10px 0 6px;font-weight:300;font-size:38px;line-height:1.1;letter-spacing:-0.015em;color:<?= $palette['ink'] ?>;">
<?= e($tree->title()) ?>
</h1>
<?php
// Strip the leading scheme so the link
// reads as a clean hostname/path — the
// anchor still points at the absolute URL.
// Header H1 → tree home page (familie tree).
// Site link below → site root, so recipients
// see a clean hostname they can paste / share.
$tree_url = route(TreePage::class, ['tree' => $tree->name()]);
$tree_url_lbl = preg_replace('~^https?://~i', '', rtrim($tree_url, '/'));
$site_url = route(HomePage::class);
$site_lbl = preg_replace('~^https?://~i', '', rtrim($site_url, '/'));
?>
<div style="margin-top:4px;font-size:13px;font-weight:400;letter-spacing:0.01em;">
<h1 style="margin:10px 0 6px;font-weight:300;font-size:38px;line-height:1.1;letter-spacing:-0.015em;color:<?= $palette['ink'] ?>;">
<a href="<?= e($tree_url) ?>"
style="color:<?= $palette['ink'] ?>;text-decoration:none;">
<?= e($tree->title()) ?>
</a>
</h1>
<div style="margin-top:4px;font-size:13px;font-weight:400;letter-spacing:0.01em;">
<a href="<?= e($site_url) ?>"
style="color:<?= $palette['link'] ?>;text-decoration:none;border-bottom:1px solid <?= $palette['link'] ?>33;">
<?= e($tree_url_lbl) ?>
<?= e($site_lbl) ?>
</a>
</div>
<div style="margin-top:6px;font-size:13px;font-weight:300;color:<?= $palette['ink3'] ?>;">