feat: let a stored preference decide the ground

`prefs.theme` is dark, light or system, defaulting to dark: a reader
whose laptop is in light mode has said something about their laptop, not
about this page, and following the device would move every existing
reader the first time they loaded this. No control yet — the value can
only arrive from storage.

App resolves it and writes it to the document. It is also where a hue
meets the theme: doing that in the lane resolver means every label, chip,
rail and bar gets the readable answer without a component knowing a theme
exists, which is why the colophon moves onto that resolver too.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-18 23:30:41 +02:00
co-authored by Claude Opus 5
parent 448c70968d
commit 3ad74df5ea
3 changed files with 53 additions and 24 deletions
+12
View File
@@ -5,6 +5,7 @@ import { guessRegion } from "../../shared/time.ts";
import type { SortMode } from "./sort.ts";
import { KEYS, readJson, writeJson } from "./storage.ts";
import type { TimelineGroup } from "./lanes.ts";
import { DEFAULT_THEME_CHOICE, type ThemeChoice } from "./theme.ts";
import { DEFAULT_DAY_WIDTH } from "./zoom.ts";
/**
@@ -98,6 +99,16 @@ export interface Prefs {
showCompleted: boolean;
/** Reveal events the reader has ignored, so they can be restored. */
showIgnored: boolean;
/**
* Which ground the app is drawn on: `dark`, `light`, or `system` to follow
* the device.
*
* Dark is the default and not a placeholder — see `DEFAULT_THEME_CHOICE`. The
* value only decides colour: nothing about what is shown, sorted, counted or
* stored changes with it, which is why it can be flipped mid-read with
* nothing to save.
*/
theme: ThemeChoice;
/** False until the reader confirms or changes the guessed region. */
regionConfirmed: boolean;
/** False until the reader has picked their games on first run. */
@@ -116,6 +127,7 @@ function defaults(): Prefs {
detectDaily: false,
showCompleted: true,
showIgnored: false,
theme: DEFAULT_THEME_CHOICE,
regionConfirmed: false,
onboarded: false,
};