From 958eb210d4ec3b307038fd7b57269c14e72ccb7e Mon Sep 17 00:00:00 2001 From: gabriel Date: Thu, 2 Jul 2026 12:45:26 +0200 Subject: [PATCH] Add automatic dark theme Follow the OS setting via prefers-color-scheme (no toggle, no JS). Most of the site is built on the --od-*/--fg/--bg token layer, so a dark override of the base color tokens in colors_and_type.css cascades to every page that uses var(...); inline styles and callouts were already tokenized or intentionally fixed (e-paper display-color swatches), so no per-page migration was needed. - colors_and_type.css: dark values for ink/paper/brand/semantic tokens and black-based shadows; swap the header logo mark to a white-outline variant via the `content` property so it tracks live scheme changes (an -referenced SVG's own media query only re-renders on reload). - logo-mark.svg: self-adapting outline (keeps the favicon in sync on load). - logo-mark-dark.svg: static white-outline variant for the live header swap. - common.css: align the legacy pages' dark palette with the main tokens and use dark ink for text on the accent button. Verified in Chrome (both schemes, live switching): no light-on-dark leaks and no dark-mode contrast regressions across all pages. --- httpdocs/assets/brand/logo-mark-dark.svg | 8 +++ httpdocs/assets/brand/logo-mark.svg | 12 ++++- httpdocs/common.css | 24 +++++---- httpdocs/css/colors_and_type.css | 64 ++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 httpdocs/assets/brand/logo-mark-dark.svg diff --git a/httpdocs/assets/brand/logo-mark-dark.svg b/httpdocs/assets/brand/logo-mark-dark.svg new file mode 100644 index 0000000..6979745 --- /dev/null +++ b/httpdocs/assets/brand/logo-mark-dark.svg @@ -0,0 +1,8 @@ + + diff --git a/httpdocs/assets/brand/logo-mark.svg b/httpdocs/assets/brand/logo-mark.svg index 8e5ea07..91f9415 100644 --- a/httpdocs/assets/brand/logo-mark.svg +++ b/httpdocs/assets/brand/logo-mark.svg @@ -1,5 +1,15 @@ diff --git a/httpdocs/common.css b/httpdocs/common.css index 66a9835..6f1e7a6 100644 --- a/httpdocs/common.css +++ b/httpdocs/common.css @@ -13,18 +13,22 @@ --border-color: #e5e7eb; } -/* Dark mode support */ +/* Dark mode support. + Palette aligned with the main site's dark tokens (css/colors_and_type.css) + so these legacy common.css pages feel cohesive when navigating to/from the + rest of the site: cool near-black surfaces, soft off-white ink, brand blue + nudged lighter for contrast. */ @media (prefers-color-scheme: dark) { :root { - --foreground: #f9fafb; - --background: #111827; - --muted-foreground: #9ca3af; - --accent: #00d4ff; - --accent-hover: #00bfff; - --accent-button: #fff; - --card-background: #1f2937; - --card-foreground: #f9fafb; - --border-color: #374151; + --foreground: #E9ECEF; /* --od-ink (dark) */ + --background: #131619; /* --od-paper (dark) */ + --muted-foreground: #949CA5; /* --od-ink-3 (dark) */ + --accent: #22C7FF; /* --od-blue (dark) */ + --accent-hover: #5AD4FF; /* --od-blue-ink (dark) */ + --accent-button: #08252F; /* dark ink for text sitting on the blue button */ + --card-background: #1B1F24; /* --od-paper-2 (dark) */ + --card-foreground: #E9ECEF; + --border-color: #2C323A; /* --od-paper-line (dark) */ } } diff --git a/httpdocs/css/colors_and_type.css b/httpdocs/css/colors_and_type.css index e374fad..5ffd073 100644 --- a/httpdocs/css/colors_and_type.css +++ b/httpdocs/css/colors_and_type.css @@ -501,6 +501,70 @@ --shadow-focus: 0 0 0 3px color-mix(in oklab, var(--od-blue) 40%, transparent); } +/* ============================================================ + Dark theme — automatic, follows the OS setting. + ------------------------------------------------------------ + We only redefine the BASE color tokens here. The semantic + aliases (--fg1..4, --bg1..3, --line, --od-info) and the + var()-based shadow/color-mix tokens re-resolve against these + automatically, so everything built on the token layer flips + without further edits. + + Palette intent: the light theme is warm cream "paper"; the + dark theme is a cool near-black "bezel" (echoing --od-bezel) + with soft off-white ink, so text never sits at pure #fff on + pure #000. Brand blue is nudged lighter for AA contrast on + dark surfaces; semantic hues are brightened for the same + reason. + ============================================================ */ + +@media (prefers-color-scheme: dark) { + :root { + /* ---------- Ink (now the LIGHT foreground) ---------- */ + --od-ink: #E9ECEF; /* fg1 — body, headings */ + --od-ink-2: #C3C9D0; /* fg2 — secondary text */ + --od-ink-3: #949CA5; /* fg3 — tertiary / captions */ + --od-ink-4: #6C757E; /* fg4 — muted / placeholder */ + --od-ink-line: #333A41; /* hairline borders */ + + /* ---------- Paper (now the DARK background) ---------- */ + --od-paper: #131619; /* page bg — cool near-black */ + --od-paper-2: #1B1F24; /* card-on-page, alt rows */ + --od-paper-3: #23282E; /* deeper panels / hero */ + --od-paper-line: #2C323A; /* paper-on-paper borders */ + --od-white: #0E1113; /* inset surface — inputs, code blocks */ + /* --od-bezel intentionally left as-is (already the dark frame) */ + + /* ---------- Brand ---------- */ + --od-blue: #22C7FF; /* keep vivid; a touch brighter on dark */ + --od-blue-ink: #5AD4FF; /* links / blue text — lighter for contrast */ + --od-blue-deep: #85DEFF; /* hover-lighten (inverse of light "pressed") */ + --od-blue-soft: #123544; /* dark-tinted chip / hover fill */ + --od-blue-wash: #0E2530; /* tint backgrounds, table-row hover */ + + /* ---------- Semantic (brightened for legibility on dark) ---------- */ + --od-ok: #3FC06F; + --od-warn: #E0A536; + --od-error: #E5695C; + + /* ---------- Shadows (black-based; light values vanish on dark) ---------- */ + --shadow-1: 0 1px 0 var(--od-paper-line), 0 1px 2px rgba(0,0,0,0.40); + --shadow-2: 0 2px 0 var(--od-paper-line), 0 4px 14px -6px rgba(0,0,0,0.55); + --shadow-hover: 0 4px 0 var(--od-paper-line), 0 8px 24px -8px rgba(0,0,0,0.65); + } + + /* Header logo mark: swap to the white-outline variant. Doing this from the + page stylesheet (rather than relying on the media query inside the SVG) + means it reacts to live OS scheme changes without a reload, matching the + rest of the token-driven theme. The `content` swap on an is honored + by Chrome/Safari; browsers that ignore it fall back to the src, whose own + internal media query still adapts on load. */ + .site-bar__mark, + .home-bar__mark { + content: url("../assets/brand/logo-mark-dark.svg"); + } +} + /* ============================================================ Semantic typography roles ============================================================ */