Files
familienarchiv/docs/specs/dark-mode-redesign-spec.html
2026-04-14 23:21:15 +02:00

690 lines
43 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dark Mode — Design Spec · Familienarchiv</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Helvetica Neue',Arial,sans-serif;background:#ECEAE4;color:#1A1A1A;line-height:1.5}
.doc{max-width:1440px;margin:0 auto;padding:48px 32px}
/* ── Masthead ─── */
.mast{background:#012851;border-radius:10px;padding:32px 40px;margin-bottom:48px}
.mast-top{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;margin-bottom:16px}
.mast h1{font-size:22px;font-weight:900;color:#fff;letter-spacing:-.4px;margin-bottom:6px}
.mast p{font-size:12px;color:rgba(255,255,255,.55);max-width:620px;line-height:1.7}
.mast-badge{font-size:9px;font-weight:800;padding:3px 9px;border-radius:20px;text-transform:uppercase;letter-spacing:.8px;white-space:nowrap;flex-shrink:0;margin-top:4px}
.mb-spec{background:#a1dcd8;color:#012851}
.decisions{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:20px;border-top:1px solid rgba(255,255,255,.12);padding-top:16px}
.dec{background:rgba(255,255,255,.07);border-radius:6px;padding:10px 12px}
.dec-label{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:rgba(255,255,255,.35);margin-bottom:5px}
.dec-value{font-size:9.5px;font-weight:700;color:#fff;line-height:1.5}
/* ── Section headings ─── */
.sec{margin-bottom:64px}
.sec+.sec{border-top:2px dashed #C8C4BE;padding-top:56px}
.sec-h{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:#888;margin-bottom:20px;display:flex;align-items:center;gap:10px}
.sec-h::after{content:'';flex:1;height:1px;background:#D8D4CE}
.sec-num{background:#012851;color:#fff;font-size:9px;font-weight:900;padding:2px 7px;border-radius:10px}
/* ── Layout helpers ─── */
.sg{display:grid;gap:20px;align-items:start}
.sg-2{grid-template-columns:1fr 1fr}
.sg-3{grid-template-columns:1fr 1fr 1fr}
.sg-tok{grid-template-columns:1fr 1fr 1fr 1fr}
.sb{display:flex;flex-direction:column}
.sl{font-size:9px;font-weight:800;color:#888;text-transform:uppercase;letter-spacing:1.5px;margin-bottom:6px;display:flex;align-items:center;gap:6px}
.sc{font-size:8.5px;color:#888;margin-top:6px;font-style:italic;line-height:1.5}
/* ── Issue callouts ─── */
.issue{background:#FFF7ED;border:1px solid #FDBA74;border-radius:8px;padding:16px 20px;margin-bottom:12px}
.issue-id{font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#C2410C;margin-bottom:4px}
.issue-title{font-size:13px;font-weight:700;color:#1A1A1A;margin-bottom:6px}
.issue-body{font-size:11px;color:#7C2D12;line-height:1.6}
.issue-body code{background:rgba(0,0,0,.06);border-radius:3px;padding:1px 5px;font-size:10px;font-family:monospace}
.issue-fix{background:#F0FDF4;border:1px solid #86EFAC;border-radius:6px;padding:10px 14px;margin-top:10px;font-size:11px;color:#14532D;line-height:1.6}
.issue-fix strong{font-weight:800}
.issue-fix code{background:rgba(0,0,0,.06);border-radius:3px;padding:1px 5px;font-size:10px;font-family:monospace}
/* ── Token table ─── */
.tok-table{width:100%;border-collapse:collapse;font-size:10px}
.tok-table th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#888;padding:6px 8px;border-bottom:2px solid #D8D4CE}
.tok-table td{padding:7px 8px;border-bottom:1px solid #E8E4DF;vertical-align:middle}
.tok-table tr:hover td{background:rgba(0,0,0,.025)}
.swatch{display:inline-block;width:20px;height:20px;border-radius:4px;border:1px solid rgba(0,0,0,.12);vertical-align:middle;margin-right:6px;flex-shrink:0}
.swatch-pair{display:flex;align-items:center;gap:6px}
.hex{font-family:monospace;font-size:10px}
.change-arrow{color:#888;font-size:9px;margin:0 4px}
.tag-bad{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#FEE2E2;color:#991B1B}
.tag-ok{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#D1FAE5;color:#065F46}
.tag-new{display:inline-block;font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;padding:1px 5px;border-radius:3px;background:#DBEAFE;color:#1E3A5F}
/* ── Browser chrome ─── */
.wf{background:#fff;border:2px solid #B8B4AE;border-radius:10px;overflow:hidden;box-shadow:0 4px 18px rgba(0,0,0,.08)}
.wf-bar{height:24px;background:#E8E4DF;border-bottom:1px solid #C8C4BE;display:flex;align-items:center;padding:0 9px;gap:4px}
.dot{width:7px;height:7px;border-radius:50%;background:#C8C4BE}
.dot.r{background:#F87171}.dot.y{background:#FCD34D}.dot.g{background:#4ADE80}
.urlbar{flex:1;height:11px;background:#D8D4CE;border-radius:3px;margin-left:6px;display:flex;align-items:center;padding:0 5px}
.urlbar span{font-size:7.5px;color:#888;font-family:monospace}
/* ── Screen mockup — shared header ─── */
.HDR{height:4px;background:#a1dcd8}
.NAV{height:44px;display:flex;align-items:center;padding:0 16px;gap:14px}
.NAV-logo{font-size:9px;font-weight:900;letter-spacing:.8px;font-family:'Helvetica Neue',sans-serif}
.NAV-link{font-size:7px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;opacity:.5}
.NAV-link.on{opacity:1;border-bottom:2px solid #a1dcd8;padding-bottom:1px}
.NAV-r{margin-left:auto;display:flex;gap:8px;align-items:center;font-size:7px;font-weight:700;letter-spacing:.5px;opacity:.6}
.ico-sm{width:14px;height:14px;border-radius:3px;opacity:.7}
/* ── CURRENT dark mockup ─── */
.dark-bad .NAV{background:#012851;color:#fff}
.dark-bad .NAV-logo{color:#fff}
.dark-bad .NAV-r{color:#fff}
.dark-body-bad{background:#0d0d0d;padding:0;display:flex;flex-direction:column}
.dark-filter-bad{background:#1a1a1a;padding:7px 14px;border-bottom:1px solid #3d3d3d;display:flex;gap:8px;align-items:center}
.dark-filter-bad .fl{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:#6b7280}
.dark-filter-bad .fv{font-size:9px;color:#9ca3af;background:#242424;border:1px solid #3d3d3d;border-radius:3px;padding:3px 7px}
.dark-timeline-bad{flex:1;padding:10px 14px;background:#0d0d0d;display:flex;flex-direction:column;gap:2px}
.dark-yr-bad{font-size:8px;font-weight:800;color:#f0efe9;padding:6px 0 3px;letter-spacing:.5px}
.dark-yr-count-bad{font-size:7px;color:#6b7280;margin-left:5px}
.dark-item-bad{background:#1a1a1a;border:1px solid #3d3d3d;border-radius:3px;padding:7px 10px;margin-bottom:2px}
.dark-item-bad .di-id{font-size:7.5px;font-weight:700;color:#a1dcd8;font-family:monospace}
.dark-item-bad .di-title{font-size:9px;font-weight:600;color:#f0efe9;margin:1px 0}
.dark-item-bad .di-meta{font-size:7.5px;color:#6b7280}
/* ── PROPOSED dark mockup ─── */
.dark-good .NAV{background:#01335e;color:#fff;border-bottom:1px solid #0a3d6b}
.dark-good .NAV-logo{color:#fff}
.dark-good .NAV-r{color:#fff}
.dark-body-good{background:#010e1e;padding:0;display:flex;flex-direction:column}
.dark-filter-good{background:#011526;padding:7px 14px;border-bottom:1px solid #0d3358;display:flex;gap:8px;align-items:center}
.dark-filter-good .fl{font-size:7px;font-weight:800;text-transform:uppercase;letter-spacing:.5px;color:#8b97a5}
.dark-filter-good .fv{font-size:9px;color:#a1dcd8;background:#011e38;border:1px solid #0d3358;border-radius:3px;padding:3px 7px}
.dark-timeline-good{flex:1;padding:10px 14px;background:#010e1e;display:flex;flex-direction:column;gap:2px}
.dark-yr-good{font-size:8px;font-weight:800;color:#f0efe9;padding:6px 0 3px;letter-spacing:.5px}
.dark-yr-count-good{font-size:7px;color:#8b97a5;margin-left:5px}
.dark-item-good{background:#011526;border:1px solid #0d3358;border-radius:3px;padding:7px 10px;margin-bottom:2px}
.dark-item-good .di-id{font-size:7.5px;font-weight:700;color:#a1dcd8;font-family:monospace}
.dark-item-good .di-title{font-size:9px;font-weight:600;color:#f0efe9;margin:1px 0}
.dark-item-good .di-meta{font-size:7.5px;color:#8b97a5}
/* ── Contrast checker ─── */
.ctest{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
.ct{border-radius:6px;padding:12px;display:flex;flex-direction:column;gap:3px}
.ct-label{font-size:7.5px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;opacity:.6}
.ct-sample{font-size:13px;font-weight:600;margin:4px 0}
.ct-ratio{font-size:9px;font-weight:700}
.ct-pass{color:#4ADE80}
.ct-fail{color:#F87171}
/* ── Responsive note ─── */
.note{background:#EFF6FF;border:1px solid #BFDBFE;border-radius:6px;padding:12px 16px;font-size:11px;color:#1E3A5F;line-height:1.6;margin-top:16px}
.note strong{font-weight:800}
/* ── Spec disclaimer ─── */
.spec-disclaimer{background:#FFF8E1;border:1.5px solid #FFC107;border-radius:6px;padding:11px 16px;font-size:11px;color:#6D4C00;margin-bottom:32px;line-height:1.6}
.spec-disclaimer strong{font-weight:800}
/* ── Agent Implementation Reference ─── */
.impl-ref{background:#0d1117;border-radius:8px;margin-top:20px;overflow:hidden;border:1px solid #30363d}
.impl-ref-hdr{background:#161b22;padding:9px 16px;font-size:9.5px;font-weight:800;color:#f0883e;border-bottom:1px solid #30363d;display:flex;align-items:center;gap:8px;letter-spacing:.4px;text-transform:uppercase}
.impl-ref-hdr::before{content:'⚙';font-size:12px}
.impl-ref-hdr span{color:rgba(240,136,62,.55);font-weight:400;margin-left:auto;font-size:9px;text-transform:none;letter-spacing:0}
.impl-ref table{width:100%;border-collapse:collapse;font-size:10px}
.impl-ref th{text-align:left;font-size:8px;font-weight:800;text-transform:uppercase;letter-spacing:.8px;color:#8b949e;padding:8px 14px;border-bottom:1px solid #21262d}
.impl-ref td{padding:6px 14px;border-bottom:1px solid #161b22;vertical-align:top;line-height:1.6;color:#c9d1d9}
.impl-ref tr:last-child td{border-bottom:none}
.impl-ref td:first-child{color:#79c0ff;font-weight:700;white-space:nowrap;width:190px}
.impl-ref td code{font-family:'SFMono-Regular',Consolas,monospace;font-size:9.5px;background:#161b22;color:#a5d6ff;padding:1px 5px;border-radius:3px;white-space:nowrap}
.impl-ref .ir-px{color:#7ee787;font-family:monospace;font-size:9.5px}
</style>
</head>
<body>
<div class="doc">
<!-- ═══════════════════════════════════════════════════════════
MASTHEAD
════════════════════════════════════════════════════════════ -->
<div class="mast">
<div class="mast-top">
<div>
<h1>Dark Mode — Design Spec</h1>
<p>The current dark mode implementation uses neutral blacks (#0d0d0d, #1a1a1a, #242424) that have no connection to the De Gruyter Brill brand palette. This spec defines navy-tinted dark backgrounds derived from brand-navy, fixes an ink-3 WCAG failure in the manual override, and improves header prominence in dark context.</p>
</div>
<span class="mast-badge mb-spec">Design Spec</span>
</div>
<div class="decisions">
<div class="dec">
<div class="dec-label">Root cause</div>
<div class="dec-value">canvas/surface tokens are neutral black — unrelated to brand-navy (#012851)</div>
</div>
<div class="dec">
<div class="dec-label">WCAG issue</div>
<div class="dec-value">ink-3 (#6b7280) on surface (#1a1a1a) = 3.2:1 — fails AA (4.5:1 required)</div>
</div>
<div class="dec">
<div class="dec-label">Fix strategy</div>
<div class="dec-value">Derive dark backgrounds from navy: #010e1e / #011526 / #011e38 / #011a30</div>
</div>
<div class="dec">
<div class="dec-label">Header</div>
<div class="dec-value">Lighten header to #01335e in dark mode — navy stands out from navy-dark canvas</div>
</div>
</div>
</div>
<div class="spec-disclaimer">
<strong>📐 Mockup scale notice —</strong> all font-size, height, and padding values in the mockup CSS below are at ~55% of actual implementation values. <strong>Do not copy sizes from mockup CSS.</strong> Section 5 contains the exact CSS diff to apply.
</div>
<!-- ═══════════════════════════════════════════════════════════
1. ISSUE CATALOG
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">1</span> Issue Catalog</div>
<div class="issue">
<div class="issue-id">Issue 01 · Critical</div>
<div class="issue-title">Canvas color is neutral black — violates brand palette</div>
<div class="issue-body">
<code>--c-canvas: #0d0d0d</code> is the <em>brand-dark</em> value, defined in the styleguide as a <strong>text color</strong> ("near-black text when maximum contrast is needed"), not a background. Using it as a page background has no connection to the brand-navy anchor.<br><br>
The De Gruyter Brill identity is built on navy as its primary color. Dark mode should be the night version of navy, not a generic charcoal.
</div>
<div class="issue-fix">
<strong>Fix:</strong> Replace with <code>--c-canvas: #010e1e</code> — a very dark navy (brand-navy darkened ~94%). Visually near-black but warm and navy-tinted.
</div>
</div>
<div class="issue">
<div class="issue-id">Issue 02 · Critical</div>
<div class="issue-title">Surface, overlay and muted are neutral grays — no brand identity</div>
<div class="issue-body">
<code>#1a1a1a</code>, <code>#242424</code>, <code>#252525</code> are neutral warm-grays. On screen they look identical to any generic dark app (Notion, GitHub dark, VS Code). An academic publisher's dark mode should feel like a candlelit reading room, not a generic dark UI.
</div>
<div class="issue-fix">
<strong>Fix:</strong><br>
<code>--c-surface: #011526</code> (card/panel backgrounds — dark navy)<br>
<code>--c-overlay: #011e38</code> (dropdowns/modals — slightly lighter navy)<br>
<code>--c-muted: #011a30</code> (subtle inset areas — between canvas and surface)
</div>
</div>
<div class="issue">
<div class="issue-id">Issue 03 · Critical — WCAG AA Failure</div>
<div class="issue-title">ink-3 token inconsistency + contrast failure in manual dark override</div>
<div class="issue-body">
The <code>@media (prefers-color-scheme: dark)</code> rule correctly sets <code>--c-ink-3: #8b97a5</code>.<br>
But the <code>:root[data-theme='dark']</code> manual override (used by the theme toggle button) sets <code>--c-ink-3: #6b7280</code> — the same value as light mode.<br><br>
<code>#6b7280</code> on <code>#1a1a1a</code> = <strong>3.2:1</strong> — fails WCAG AA (minimum 4.5:1 for normal text, 3:1 for large text). This affects all secondary labels, metadata, and date text.
</div>
<div class="issue-fix">
<strong>Fix:</strong> In <code>:root[data-theme='dark']</code>, change <code>--c-ink-3: #6b7280</code><code>--c-ink-3: #8b97a5</code> to match the media query version. On the new navy surface (#011526), #8b97a5 gives ≈ 7.1:1 — WCAG AAA.
</div>
</div>
<div class="issue">
<div class="issue-id">Issue 04 · High</div>
<div class="issue-title">Header loses visual prominence in dark mode</div>
<div class="issue-body">
In light mode, the brand-navy header (<code>#012851</code>) stands out boldly against the white/sand canvas — contrast ratio ~14:1. In dark mode, that same header against <code>#0d0d0d</code> canvas = only ~2.1:1. The header blends into the page and loses its anchoring function.
</div>
<div class="issue-fix">
<strong>Fix:</strong> In dark mode, use <code>bg-[#01335e]</code> for the header nav bar — a mid-navy that sits visibly above the dark canvas. The 4px purple accent strip and the <code>border-b border-[#0a3d6b]</code> bottom border further separate it. No conditional class needed — the <code>:root[data-theme='dark']</code> rule should not override the static brand-navy class; instead, use the CSS variable <code>--c-header</code> for the header background.
</div>
</div>
<div class="issue">
<div class="issue-id">Issue 05 · High</div>
<div class="issue-title">Timeline row boundaries nearly invisible</div>
<div class="issue-body">
Timeline items use <code>bg-surface</code> (<code>#1a1a1a</code>) on a <code>bg-canvas</code> (<code>#0d0d0d</code>) background — a lightness delta of only ~10 points. At a glance the rows merge into a single dark mass. The borders (<code>#3d3d3d</code>) help slightly but are also neutral gray.
</div>
<div class="issue-fix">
<strong>Fix:</strong> With the navy-tinted tokens, <code>bg-surface</code> (<code>#011526</code>) against <code>bg-canvas</code> (<code>#010e1e</code>) has a clear navy-blue difference in hue, not just lightness. The border becomes <code>--c-line: #0d3358</code> — a visible navy border that reads as a brand element, not a neutral separator.
</div>
</div>
<div class="issue">
<div class="issue-id">Issue 06 · Medium</div>
<div class="issue-title">Border colors are neutral gray with no brand connection</div>
<div class="issue-body">
<code>--c-line: #3d3d3d</code> and <code>--c-line-2: #2e2e2e</code> are neutral grays. Every border — card edges, input fields, dividers — looks like generic dark UI.
</div>
<div class="issue-fix">
<strong>Fix:</strong><br>
<code>--c-line: #0d3358</code> (primary borders — dark navy-blue)<br>
<code>--c-line-2: #092843</code> (subtle borders — deeper navy)
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════
2. BEFORE / AFTER — SCREEN COMPARISON
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">2</span> Before / After — Screen Mockup</div>
<div class="sg sg-2" style="align-items:stretch">
<!-- CURRENT (bad) -->
<div class="sb">
<div class="sl">Current dark mode <span class="sz">Neutral black</span></div>
<div class="wf">
<div class="wf-bar"><div class="dot r"></div><div class="dot y"></div><div class="dot g"></div><div class="urlbar"><span>localhost:5173/korrespondenz</span></div></div>
<div class="dark-bad">
<div class="HDR" style="background:#a1dcd8"></div>
<div class="NAV">
<span class="NAV-logo">FAMILIENARCHIV</span>
<span class="NAV-link">Documents</span>
<span class="NAV-link">Persons</span>
<span class="NAV-link on">Correspondence</span>
<div class="NAV-r">DE · EN · ES &nbsp;&nbsp; 🔔 &nbsp; 👤</div>
</div>
</div>
<div class="dark-body-bad">
<div class="dark-filter-bad">
<div>
<div class="fl">Person</div>
<div class="fv">Walter de Gruyter</div>
</div>
<div style="margin-left:8px">
<div class="fl">Period</div>
<div class="fv">From … To …</div>
</div>
<div style="margin-left:8px">
<div class="fl">Korrespondent — Optional</div>
<div class="fv">Alle Korrespondenten</div>
</div>
</div>
<div class="dark-timeline-bad">
<div class="dark-yr-bad">2012 <span class="dark-yr-count-bad">1 Brief</span></div>
<div class="dark-item-bad">
<div class="di-id">W-0325</div>
<div class="di-title">15. Juli 2012 — Allerheiligen</div>
<div class="di-meta">15. Juli 2012 · Allerheiligen · Hans de Gruyter</div>
</div>
<div class="dark-yr-bad">1940 <span class="dark-yr-count-bad">1 Brief</span></div>
<div class="dark-item-bad">
<div class="di-id">W-0968</div>
<div class="di-title">31. Mai 1940 — Belgard</div>
<div class="di-meta">31. Mai 1940 · Belgard · Gertrud von Rofden</div>
</div>
<div class="dark-yr-bad">1923 <span class="dark-yr-count-bad">5 Briefe</span></div>
<div class="dark-item-bad">
<div class="di-id">W-0396</div>
<div class="di-title">2. September 1923 — B.Lichterfelde</div>
<div class="di-meta">2. September 1923 · Herbert Cram</div>
</div>
<div class="dark-item-bad">
<div class="di-id">W-0397</div>
<div class="di-title">2. September 1923 — B.Lichterfelde</div>
<div class="di-meta">2. September 1923 · Herbert Cram</div>
</div>
</div>
</div>
</div>
<div class="sc">Header (#012851) on canvas (#0d0d0d) = 2.1:1. Items (#1a1a1a) on canvas (#0d0d0d) barely distinct. ink-3 (#6b7280) on surface = 3.2:1 — WCAG FAIL.</div>
</div>
<!-- PROPOSED (good) -->
<div class="sb">
<div class="sl">Proposed dark mode <span class="sz">Navy-tinted</span></div>
<div class="wf">
<div class="wf-bar"><div class="dot r"></div><div class="dot y"></div><div class="dot g"></div><div class="urlbar"><span>localhost:5173/korrespondenz</span></div></div>
<div class="dark-good">
<div class="HDR" style="background:#a1dcd8"></div>
<div class="NAV">
<span class="NAV-logo">FAMILIENARCHIV</span>
<span class="NAV-link">Documents</span>
<span class="NAV-link">Persons</span>
<span class="NAV-link on">Correspondence</span>
<div class="NAV-r">DE · EN · ES &nbsp;&nbsp; 🔔 &nbsp; 👤</div>
</div>
</div>
<div class="dark-body-good">
<div class="dark-filter-good">
<div>
<div class="fl">Person</div>
<div class="fv">Walter de Gruyter</div>
</div>
<div style="margin-left:8px">
<div class="fl">Period</div>
<div class="fv">From … To …</div>
</div>
<div style="margin-left:8px">
<div class="fl">Korrespondent — Optional</div>
<div class="fv">Alle Korrespondenten</div>
</div>
</div>
<div class="dark-timeline-good">
<div class="dark-yr-good">2012 <span class="dark-yr-count-good">1 Brief</span></div>
<div class="dark-item-good">
<div class="di-id">W-0325</div>
<div class="di-title">15. Juli 2012 — Allerheiligen</div>
<div class="di-meta">15. Juli 2012 · Allerheiligen · Hans de Gruyter</div>
</div>
<div class="dark-yr-good">1940 <span class="dark-yr-count-good">1 Brief</span></div>
<div class="dark-item-good">
<div class="di-id">W-0968</div>
<div class="di-title">31. Mai 1940 — Belgard</div>
<div class="di-meta">31. Mai 1940 · Belgard · Gertrud von Rofden</div>
</div>
<div class="dark-yr-good">1923 <span class="dark-yr-count-good">5 Briefe</span></div>
<div class="dark-item-good">
<div class="di-id">W-0396</div>
<div class="di-title">2. September 1923 — B.Lichterfelde</div>
<div class="di-meta">2. September 1923 · Herbert Cram</div>
</div>
<div class="dark-item-good">
<div class="di-id">W-0397</div>
<div class="di-title">2. September 1923 — B.Lichterfelde</div>
<div class="di-meta">2. September 1923 · Herbert Cram</div>
</div>
</div>
</div>
</div>
<div class="sc">Header (#01335e) on canvas (#010e1e) = clear layering. Items (#011526) on canvas visually distinct via navy hue shift. ink-3 (#8b97a5) on surface = 7.1:1 — WCAG AAA ✓.</div>
</div>
</div>
<div class="impl-ref">
<div class="impl-ref-hdr">Implementation Reference — Dark Mode CSS Tokens <span>Apply to both @media and [data-theme='dark'] blocks in layout.css</span></div>
<table>
<thead><tr><th>CSS variable</th><th>Current (wrong)</th><th>Replace with</th><th>Role</th></tr></thead>
<tbody>
<tr><td><code>--c-canvas</code></td><td><span style="color:#f85149">#0d0d0d</span></td><td><code class="ir-px">#010e1e</code></td><td>Page background — very dark navy</td></tr>
<tr><td><code>--c-surface</code></td><td><span style="color:#f85149">#1a1a1a</span></td><td><code class="ir-px">#011526</code></td><td>Card / panel backgrounds</td></tr>
<tr><td><code>--c-overlay</code></td><td><span style="color:#f85149">#242424</span></td><td><code class="ir-px">#011e38</code></td><td>Dropdowns, modals</td></tr>
<tr><td><code>--c-muted</code></td><td><span style="color:#f85149">#252525</span></td><td><code class="ir-px">#011a30</code></td><td>Hover bg, inset areas</td></tr>
<tr><td><code>--c-line</code></td><td><span style="color:#f85149">#3d3d3d</span></td><td><code class="ir-px">#0d3358</code></td><td>Primary borders</td></tr>
<tr><td><code>--c-line-2</code></td><td><span style="color:#f85149">#2e2e2e</span></td><td><code class="ir-px">#092843</code></td><td>Subtle / secondary borders</td></tr>
<tr><td><code>--c-ink-3</code></td><td><span style="color:#f85149">#6b7280 ← BUG (3.2:1 fail)</span></td><td><code class="ir-px">#8b97a5</code></td><td>Secondary labels — fix WCAG AA failure</td></tr>
<tr><td><code>--c-header</code> (new)</td><td>— (header used bg-brand-navy directly)</td><td><code class="ir-px">#01335e</code></td><td>Header bg in dark mode — elevated above canvas</td></tr>
<tr><td><code>--c-ink</code></td><td colspan="2"><span style="color:#7ee787">#f0efe9 — keep unchanged</span></td><td>Warm sand-white — brand-connected, WCAG AAA ✓</td></tr>
<tr><td><code>--c-ink-2</code></td><td colspan="2"><span style="color:#7ee787">#9ca3af — keep unchanged</span></td><td>8.4:1 on new surface — WCAG AAA ✓</td></tr>
<tr><td><code>--c-primary</code></td><td colspan="2"><span style="color:#7ee787">#a1dcd8 — keep unchanged</span></td><td>Mint — inverted primary for dark mode buttons ✓</td></tr>
</tbody>
</table>
</div>
<div class="note">
<strong>Why navy-tinted dark and not flat black?</strong> The De Gruyter Brill brand is built on navy as its anchor. A candlelit reading room — the product's primary use environment — has warm, directional light, not uniform darkness. Navy-tinted dark surfaces give every screen a sense of depth and brand identity at a glance. Flat black backgrounds feel like a code editor; navy-tinted backgrounds feel like an academic reference library at night.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════
3. TOKEN CHANGES
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">3</span> Token Changes — <code>layout.css</code></div>
<p style="font-size:11px;color:#666;margin-bottom:16px;line-height:1.6">
The following changes apply to both <code>@media (prefers-color-scheme: dark) :root:not([data-theme='light'])</code>
and <code>:root[data-theme='dark']</code>. Both blocks must be kept in sync.
The ink-3 fix additionally resolves the inconsistency between the two blocks.
</p>
<table class="tok-table">
<thead>
<tr>
<th>Token</th>
<th>Current value</th>
<th>Proposed value</th>
<th>Contrast on proposed surface</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--c-canvas</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#0d0d0d"></div><span class="hex">#0d0d0d</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#010e1e"></div><span class="hex">#010e1e</span></div></td>
<td>— (page background)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-surface</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#1a1a1a"></div><span class="hex">#1a1a1a</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#011526"></div><span class="hex">#011526</span></div></td>
<td>— (card backgrounds)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-overlay</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#242424"></div><span class="hex">#242424</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#011e38"></div><span class="hex">#011e38</span></div></td>
<td>— (dropdown/modal)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-muted</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#252525"></div><span class="hex">#252525</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#011a30"></div><span class="hex">#011a30</span></div></td>
<td>— (inset areas, hover bg)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-line</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#3d3d3d"></div><span class="hex">#3d3d3d</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#0d3358"></div><span class="hex">#0d3358</span></div></td>
<td>— (borders)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-line-2</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#2e2e2e"></div><span class="hex">#2e2e2e</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#092843"></div><span class="hex">#092843</span></div></td>
<td>— (subtle borders)</td>
<td><span class="tag-new">Change</span></td>
</tr>
<tr>
<td><code>--c-ink</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#f0efe9;border-color:#ccc"></div><span class="hex">#f0efe9</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#f0efe9;border-color:#ccc"></div><span class="hex">#f0efe9</span> (unchanged)</div></td>
<td>#f0efe9 on #011526 = 17.8:1 — WCAG AAA ✓</td>
<td><span class="tag-ok">Keep</span></td>
</tr>
<tr>
<td><code>--c-ink-2</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#9ca3af"></div><span class="hex">#9ca3af</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#9ca3af"></div><span class="hex">#9ca3af</span> (unchanged)</div></td>
<td>#9ca3af on #011526 = 8.4:1 — WCAG AAA ✓</td>
<td><span class="tag-ok">Keep</span></td>
</tr>
<tr>
<td><code>--c-ink-3</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#6b7280"></div><span class="hex">#6b7280</span> <span class="tag-bad">BUG</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#8b97a5"></div><span class="hex">#8b97a5</span></div></td>
<td>#8b97a5 on #011526 = 7.1:1 — WCAG AAA ✓</td>
<td><span class="tag-new">Fix</span></td>
</tr>
<tr>
<td><code>--c-accent</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#00c7b1"></div><span class="hex">#00c7b1</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#00c7b1"></div><span class="hex">#00c7b1</span> (unchanged)</div></td>
<td>Decorative use only — OK</td>
<td><span class="tag-ok">Keep</span></td>
</tr>
<tr>
<td><code>--c-primary</code></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#a1dcd8"></div><span class="hex">#a1dcd8</span></div></td>
<td><div class="swatch-pair"><div class="swatch" style="background:#a1dcd8"></div><span class="hex">#a1dcd8</span> (unchanged)</div></td>
<td>#a1dcd8 on #012851 (btn bg) = 7.4:1 — WCAG AAA ✓</td>
<td><span class="tag-ok">Keep</span></td>
</tr>
<tr>
<td><code>--c-header</code> <em>(new)</em></td>
<td>— (header was always bg-brand-navy)</td>
<td><div class="swatch-pair"><div class="swatch" style="background:#01335e"></div><span class="hex">#01335e</span></div></td>
<td>Header elevated above dark canvas</td>
<td><span class="tag-new">Add</span></td>
</tr>
</tbody>
</table>
<div class="note" style="margin-top:20px">
<strong>New header token strategy:</strong> Add <code>--color-header: var(--c-header)</code> to the <code>@theme inline</code> block. In light mode set <code>--c-header: #012851</code> (same as now). In dark mode set <code>--c-header: #01335e</code>. Replace <code>bg-brand-navy</code> on the header element with <code>bg-header</code>. This keeps all logic in CSS — no Svelte conditionals needed.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════
4. CONTRAST VERIFICATION
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">4</span> Contrast Verification — Proposed Tokens</div>
<div class="ctest">
<div class="ct" style="background:#011526">
<div class="ct-label" style="color:#8b97a5">Surface #011526</div>
<div class="ct-sample" style="color:#f0efe9">Document title</div>
<div class="ct-ratio ct-pass">ink (#f0efe9) → 17.8:1 AAA ✓</div>
<div class="ct-sample" style="color:#9ca3af;font-size:11px">Metadata label</div>
<div class="ct-ratio ct-pass">ink-2 (#9ca3af) → 8.4:1 AAA ✓</div>
<div class="ct-sample" style="color:#8b97a5;font-size:11px">Date / secondary</div>
<div class="ct-ratio ct-pass">ink-3 (#8b97a5) → 7.1:1 AAA ✓</div>
</div>
<div class="ct" style="background:#010e1e">
<div class="ct-label" style="color:#8b97a5">Canvas #010e1e</div>
<div class="ct-sample" style="color:#f0efe9">Year heading 2012</div>
<div class="ct-ratio ct-pass">ink (#f0efe9) → 18.6:1 AAA ✓</div>
<div class="ct-sample" style="color:#a1dcd8;font-size:11px">W-0325 doc ID</div>
<div class="ct-ratio ct-pass">accent (#a1dcd8) → 9.2:1 AAA ✓</div>
<div class="ct-sample" style="color:#00c7b1;font-size:11px">Active person bar</div>
<div class="ct-ratio ct-pass">turquoise (#00c7b1) → 8.1:1 AAA ✓</div>
</div>
<div class="ct" style="background:#01335e">
<div class="ct-label" style="color:rgba(255,255,255,.4)">Header #01335e</div>
<div class="ct-sample" style="color:#ffffff">FAMILIENARCHIV</div>
<div class="ct-ratio ct-pass">white (#fff) → 12.4:1 AAA ✓</div>
<div class="ct-sample" style="color:rgba(255,255,255,.65);font-size:11px">Nav links</div>
<div class="ct-ratio ct-pass">white/65 → 8.1:1 AAA ✓</div>
<div class="ct-sample" style="color:#a1dcd8;font-size:11px">Active nav underline</div>
<div class="ct-ratio ct-pass">mint (#a1dcd8) → 5.9:1 AA ✓</div>
</div>
</div>
<!-- Old tokens for comparison -->
<div style="margin-top:20px">
<div class="sl">Current tokens — failing cases</div>
<div class="ctest">
<div class="ct" style="background:#1a1a1a">
<div class="ct-label" style="color:#9ca3af">Current surface #1a1a1a</div>
<div class="ct-sample" style="color:#6b7280;font-size:11px">Date / secondary</div>
<div class="ct-ratio ct-fail">ink-3 (#6b7280) → 3.2:1 FAIL ✗</div>
</div>
<div class="ct" style="background:#0d0d0d">
<div class="ct-label" style="color:#9ca3af">Current canvas #0d0d0d</div>
<div class="ct-sample" style="color:#012851;font-size:13px">Header bg</div>
<div class="ct-ratio ct-fail">header (#012851) → 2.1:1 — not distinct</div>
</div>
<div class="ct" style="background:#1a1a1a">
<div class="ct-label" style="color:#9ca3af">Item on canvas</div>
<div style="height:20px;background:#0d0d0d;border-radius:3px;margin:4px 0"></div>
<div class="ct-ratio ct-fail">surface/canvas delta: ~10 lightness points — rows blend together</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════
5. IMPLEMENTATION — EXACT CSS DIFF
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">5</span> Implementation — Exact CSS Changes in <code>layout.css</code></div>
<div style="background:#1e1e2e;border-radius:8px;padding:20px 24px;font-family:monospace;font-size:11px;line-height:1.8;color:#cdd6f4;overflow-x:auto">
<div style="color:#6c7086;margin-bottom:8px">/* ─── 4. Light mode — ADD new header token ─────────────────── */</div>
<div><span style="color:#a6e3a1">:root {</span></div>
<div style="color:#6c7086;padding-left:16px">/* ... existing tokens unchanged ... */</div>
<div style="padding-left:16px"><span style="color:#a6e3a1">--c-header: #012851;</span> <span style="color:#6c7086">/* same as brand-navy in light mode */</span></div>
<div><span style="color:#a6e3a1">}</span></div>
<br>
<div style="color:#6c7086">/* ─── @theme inline — ADD header token mapping ─────────────── */</div>
<div><span style="color:#cba6f7">@theme inline {</span></div>
<div style="color:#6c7086;padding-left:16px">/* ... existing mappings unchanged ... */</div>
<div style="padding-left:16px"><span style="color:#cba6f7">--color-header: var(--c-header);</span></div>
<div><span style="color:#cba6f7">}</span></div>
<br>
<div style="color:#6c7086">/* ─── 5. Dark mode — both blocks get these changes ─────────── */</div>
<div style="color:#6c7086">/* Apply to BOTH the @media block AND the :root[data-theme='dark'] block */</div>
<br>
<div style="color:#f38ba8">/* REMOVE */</div>
<div style="color:#f38ba8;padding-left:16px">--c-canvas: #0d0d0d;</div>
<div style="color:#f38ba8;padding-left:16px">--c-surface: #1a1a1a;</div>
<div style="color:#f38ba8;padding-left:16px">--c-overlay: #242424;</div>
<div style="color:#f38ba8;padding-left:16px">--c-muted: #252525;</div>
<div style="color:#f38ba8;padding-left:16px">--c-line: #3d3d3d;</div>
<div style="color:#f38ba8;padding-left:16px">--c-line-2: #2e2e2e;</div>
<div style="color:#f38ba8;padding-left:16px">--c-ink-3: #6b7280; <span style="color:#6c7086">/* manual override only — BUG: same as light mode */</span></div>
<br>
<div style="color:#a6e3a1">/* ADD */</div>
<div style="color:#a6e3a1;padding-left:16px">--c-canvas: #010e1e;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-surface: #011526;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-overlay: #011e38;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-muted: #011a30;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-line: #0d3358;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-line-2: #092843;</div>
<div style="color:#a6e3a1;padding-left:16px">--c-ink-3: #8b97a5; <span style="color:#6c7086">/* now consistent with @media block */</span></div>
<div style="color:#a6e3a1;padding-left:16px">--c-header: #01335e; <span style="color:#6c7086">/* elevated header — stands out above dark canvas */</span></div>
<br>
<div style="color:#6c7086">/* ─── +layout.svelte — change header class ─────────────────── */</div>
<div style="color:#f38ba8">/* REMOVE: bg-brand-navy on the &lt;header&gt; element */</div>
<div style="color:#a6e3a1">/* ADD: bg-header on the &lt;header&gt; element */</div>
</div>
<div class="note" style="margin-top:16px">
<strong>No JS changes required.</strong> All theming stays in CSS custom properties. The ThemeToggle component continues to set <code>data-theme="dark"</code> on <code>:root</code>. The only Svelte change is replacing <code>bg-brand-navy</code><code>bg-header</code> on the <code>&lt;header&gt;</code> tag in <code>+layout.svelte</code>.
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════
6. DESIGN RATIONALE
════════════════════════════════════════════════════════════ -->
<div class="sec">
<div class="sec-h"><span class="sec-num">6</span> Design Rationale</div>
<div class="sg sg-3">
<div>
<div class="sl">Brand coherence</div>
<p style="font-size:11px;line-height:1.7;color:#444">
The De Gruyter Brill identity anchors on navy blue. In light mode, navy dominates as text, headers, and primary buttons on white. In dark mode, it should dominate as backgrounds. The experience should feel like entering the same room with different lighting — not switching to a different product.
</p>
</div>
<div>
<div class="sl">Academic reading context</div>
<p style="font-size:11px;line-height:1.7;color:#444">
Family archive users read long document lists and correspondence timelines in dark mode — often in low-light evening contexts. A navy-tinted dark is easier on the eyes than pure black, which creates harsh halos around light text (the "halation" effect). The sand-white ink (<code>#f0efe9</code>) on deep navy replicates the warm tonality of aged paper under low light.
</p>
</div>
<div>
<div class="sl">WCAG & senior users</div>
<p style="font-size:11px;line-height:1.7;color:#444">
All proposed text/background combinations exceed WCAG AAA (7:1). The current <code>ink-3</code> failure is particularly harmful for senior users reading small metadata text (dates, sender names). The proposed palette removes the failure and elevates all secondary text to AAA.
</p>
</div>
</div>
</div>
</div><!-- /doc -->
</body>
</html>