Dark Mode — Design Spec

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.

Design Spec
Root cause
canvas/surface tokens are neutral black — unrelated to brand-navy (#012851)
WCAG issue
ink-3 (#6b7280) on surface (#1a1a1a) = 3.2:1 — fails AA (4.5:1 required)
Fix strategy
Derive dark backgrounds from navy: #010e1e / #011526 / #011e38 / #011a30
Header
Lighten header to #01335e in dark mode — navy stands out from navy-dark canvas
📐 Mockup scale notice — all font-size, height, and padding values in the mockup CSS below are at ~55% of actual implementation values. Do not copy sizes from mockup CSS. Section 5 contains the exact CSS diff to apply.
1 Issue Catalog
Issue 01 · Critical
Canvas color is neutral black — violates brand palette
--c-canvas: #0d0d0d is the brand-dark value, defined in the styleguide as a text color ("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.

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.
Fix: Replace with --c-canvas: #010e1e — a very dark navy (brand-navy darkened ~94%). Visually near-black but warm and navy-tinted.
Issue 02 · Critical
Surface, overlay and muted are neutral grays — no brand identity
#1a1a1a, #242424, #252525 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.
Fix:
--c-surface: #011526 (card/panel backgrounds — dark navy)
--c-overlay: #011e38 (dropdowns/modals — slightly lighter navy)
--c-muted: #011a30 (subtle inset areas — between canvas and surface)
Issue 03 · Critical — WCAG AA Failure
ink-3 token inconsistency + contrast failure in manual dark override
The @media (prefers-color-scheme: dark) rule correctly sets --c-ink-3: #8b97a5.
But the :root[data-theme='dark'] manual override (used by the theme toggle button) sets --c-ink-3: #6b7280 — the same value as light mode.

#6b7280 on #1a1a1a = 3.2:1 — fails WCAG AA (minimum 4.5:1 for normal text, 3:1 for large text). This affects all secondary labels, metadata, and date text.
Fix: In :root[data-theme='dark'], change --c-ink-3: #6b7280--c-ink-3: #8b97a5 to match the media query version. On the new navy surface (#011526), #8b97a5 gives ≈ 7.1:1 — WCAG AAA.
Issue 04 · High
Header loses visual prominence in dark mode
In light mode, the brand-navy header (#012851) stands out boldly against the white/sand canvas — contrast ratio ~14:1. In dark mode, that same header against #0d0d0d canvas = only ~2.1:1. The header blends into the page and loses its anchoring function.
Fix: In dark mode, use bg-[#01335e] for the header nav bar — a mid-navy that sits visibly above the dark canvas. The 4px purple accent strip and the border-b border-[#0a3d6b] bottom border further separate it. No conditional class needed — the :root[data-theme='dark'] rule should not override the static brand-navy class; instead, use the CSS variable --c-header for the header background.
Issue 05 · High
Timeline row boundaries nearly invisible
Timeline items use bg-surface (#1a1a1a) on a bg-canvas (#0d0d0d) background — a lightness delta of only ~10 points. At a glance the rows merge into a single dark mass. The borders (#3d3d3d) help slightly but are also neutral gray.
Fix: With the navy-tinted tokens, bg-surface (#011526) against bg-canvas (#010e1e) has a clear navy-blue difference in hue, not just lightness. The border becomes --c-line: #0d3358 — a visible navy border that reads as a brand element, not a neutral separator.
Issue 06 · Medium
Border colors are neutral gray with no brand connection
--c-line: #3d3d3d and --c-line-2: #2e2e2e are neutral grays. Every border — card edges, input fields, dividers — looks like generic dark UI.
Fix:
--c-line: #0d3358 (primary borders — dark navy-blue)
--c-line-2: #092843 (subtle borders — deeper navy)
2 Before / After — Screen Mockup
Current dark mode Neutral black
localhost:5173/korrespondenz
Person
Walter de Gruyter
Period
From … To …
Korrespondent — Optional
Alle Korrespondenten
2012 1 Brief
W-0325
15. Juli 2012 — Allerheiligen
15. Juli 2012 · Allerheiligen · Hans de Gruyter
1940 1 Brief
W-0968
31. Mai 1940 — Belgard
31. Mai 1940 · Belgard · Gertrud von Rofden
1923 5 Briefe
W-0396
2. September 1923 — B.Lichterfelde
2. September 1923 · Herbert Cram
W-0397
2. September 1923 — B.Lichterfelde
2. September 1923 · Herbert Cram
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.
Proposed dark mode Navy-tinted
localhost:5173/korrespondenz
Person
Walter de Gruyter
Period
From … To …
Korrespondent — Optional
Alle Korrespondenten
2012 1 Brief
W-0325
15. Juli 2012 — Allerheiligen
15. Juli 2012 · Allerheiligen · Hans de Gruyter
1940 1 Brief
W-0968
31. Mai 1940 — Belgard
31. Mai 1940 · Belgard · Gertrud von Rofden
1923 5 Briefe
W-0396
2. September 1923 — B.Lichterfelde
2. September 1923 · Herbert Cram
W-0397
2. September 1923 — B.Lichterfelde
2. September 1923 · Herbert Cram
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 ✓.
Implementation Reference — Dark Mode CSS Tokens Apply to both @media and [data-theme='dark'] blocks in layout.css
CSS variableCurrent (wrong)Replace withRole
--c-canvas#0d0d0d#010e1ePage background — very dark navy
--c-surface#1a1a1a#011526Card / panel backgrounds
--c-overlay#242424#011e38Dropdowns, modals
--c-muted#252525#011a30Hover bg, inset areas
--c-line#3d3d3d#0d3358Primary borders
--c-line-2#2e2e2e#092843Subtle / secondary borders
--c-ink-3#6b7280 ← BUG (3.2:1 fail)#8b97a5Secondary labels — fix WCAG AA failure
--c-header (new)— (header used bg-brand-navy directly)#01335eHeader bg in dark mode — elevated above canvas
--c-ink#f0efe9 — keep unchangedWarm sand-white — brand-connected, WCAG AAA ✓
--c-ink-2#9ca3af — keep unchanged8.4:1 on new surface — WCAG AAA ✓
--c-primary#a1dcd8 — keep unchangedMint — inverted primary for dark mode buttons ✓
Why navy-tinted dark and not flat black? 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.
3 Token Changes — layout.css

The following changes apply to both @media (prefers-color-scheme: dark) :root:not([data-theme='light']) and :root[data-theme='dark']. Both blocks must be kept in sync. The ink-3 fix additionally resolves the inconsistency between the two blocks.

Token Current value Proposed value Contrast on proposed surface Status
--c-canvas
#0d0d0d
#010e1e
— (page background) Change
--c-surface
#1a1a1a
#011526
— (card backgrounds) Change
--c-overlay
#242424
#011e38
— (dropdown/modal) Change
--c-muted
#252525
#011a30
— (inset areas, hover bg) Change
--c-line
#3d3d3d
#0d3358
— (borders) Change
--c-line-2
#2e2e2e
#092843
— (subtle borders) Change
--c-ink
#f0efe9
#f0efe9 (unchanged)
#f0efe9 on #011526 = 17.8:1 — WCAG AAA ✓ Keep
--c-ink-2
#9ca3af
#9ca3af (unchanged)
#9ca3af on #011526 = 8.4:1 — WCAG AAA ✓ Keep
--c-ink-3
#6b7280 BUG
#8b97a5
#8b97a5 on #011526 = 7.1:1 — WCAG AAA ✓ Fix
--c-accent
#00c7b1
#00c7b1 (unchanged)
Decorative use only — OK Keep
--c-primary
#a1dcd8
#a1dcd8 (unchanged)
#a1dcd8 on #012851 (btn bg) = 7.4:1 — WCAG AAA ✓ Keep
--c-header (new) — (header was always bg-brand-navy)
#01335e
Header elevated above dark canvas Add
New header token strategy: Add --color-header: var(--c-header) to the @theme inline block. In light mode set --c-header: #012851 (same as now). In dark mode set --c-header: #01335e. Replace bg-brand-navy on the header element with bg-header. This keeps all logic in CSS — no Svelte conditionals needed.
4 Contrast Verification — Proposed Tokens
Surface #011526
Document title
ink (#f0efe9) → 17.8:1 AAA ✓
Metadata label
ink-2 (#9ca3af) → 8.4:1 AAA ✓
Date / secondary
ink-3 (#8b97a5) → 7.1:1 AAA ✓
Canvas #010e1e
Year heading 2012
ink (#f0efe9) → 18.6:1 AAA ✓
W-0325 doc ID
accent (#a1dcd8) → 9.2:1 AAA ✓
Active person bar
turquoise (#00c7b1) → 8.1:1 AAA ✓
Header #01335e
FAMILIENARCHIV
white (#fff) → 12.4:1 AAA ✓
Nav links
white/65 → 8.1:1 AAA ✓
Active nav underline
mint (#a1dcd8) → 5.9:1 AA ✓
Current tokens — failing cases
Current surface #1a1a1a
Date / secondary
ink-3 (#6b7280) → 3.2:1 FAIL ✗
Current canvas #0d0d0d
Header bg
header (#012851) → 2.1:1 — not distinct
Item on canvas
surface/canvas delta: ~10 lightness points — rows blend together
5 Implementation — Exact CSS Changes in layout.css
/* ─── 4. Light mode — ADD new header token ─────────────────── */
:root {
/* ... existing tokens unchanged ... */
--c-header: #012851; /* same as brand-navy in light mode */
}

/* ─── @theme inline — ADD header token mapping ─────────────── */
@theme inline {
/* ... existing mappings unchanged ... */
--color-header: var(--c-header);
}

/* ─── 5. Dark mode — both blocks get these changes ─────────── */
/* Apply to BOTH the @media block AND the :root[data-theme='dark'] block */

/* REMOVE */
--c-canvas: #0d0d0d;
--c-surface: #1a1a1a;
--c-overlay: #242424;
--c-muted: #252525;
--c-line: #3d3d3d;
--c-line-2: #2e2e2e;
--c-ink-3: #6b7280; /* manual override only — BUG: same as light mode */

/* ADD */
--c-canvas: #010e1e;
--c-surface: #011526;
--c-overlay: #011e38;
--c-muted: #011a30;
--c-line: #0d3358;
--c-line-2: #092843;
--c-ink-3: #8b97a5; /* now consistent with @media block */
--c-header: #01335e; /* elevated header — stands out above dark canvas */

/* ─── +layout.svelte — change header class ─────────────────── */
/* REMOVE: bg-brand-navy on the <header> element */
/* ADD: bg-header on the <header> element */
No JS changes required. All theming stays in CSS custom properties. The ThemeToggle component continues to set data-theme="dark" on :root. The only Svelte change is replacing bg-brand-navybg-header on the <header> tag in +layout.svelte.
6 Design Rationale
Brand coherence

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.

Academic reading context

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 (#f0efe9) on deep navy replicates the warm tonality of aged paper under low light.

WCAG & senior users

All proposed text/background combinations exceed WCAG AAA (7:1). The current ink-3 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.