From 3ddeb072c6c81dbbbcfb20e5306a7365ba1445c4 Mon Sep 17 00:00:00 2001 From: Martin Wedvich Date: Tue, 12 May 2026 23:17:30 +0200 Subject: [PATCH] feat(web): apply color-scheme preference before paint via inline IIFE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an inline pre-paint script in packages/web/index.html that reads a 'color-scheme' preference from localStorage, resolves 'system' against prefers-color-scheme, and sets data-color-scheme-pref / data-color-scheme on plus the CSS color-scheme property. No FOUC; no React work yet. Also lands docs/user-settings.md as the spec for per-user, non-PII client preferences — covers naming, DOM exposure, and the deferred event-sourced shape for cross-device sync. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 1 + docs/user-settings.md | 92 +++++++++++++++++++++++++++++++++++++++++ packages/web/index.html | 15 +++++++ 3 files changed, 108 insertions(+) create mode 100644 docs/user-settings.md diff --git a/CLAUDE.md b/CLAUDE.md index 74bb300..5b5181a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -78,3 +78,4 @@ Filter to a specific package with `-F`: - [docs/tech-stack.md](docs/tech-stack.md) - [docs/testing-guide.md](docs/testing-guide.md) - [docs/testing.md](docs/testing.md) +- [docs/user-settings.md](docs/user-settings.md) diff --git a/docs/user-settings.md b/docs/user-settings.md new file mode 100644 index 0000000..7d8d464 --- /dev/null +++ b/docs/user-settings.md @@ -0,0 +1,92 @@ +# User Settings + +How Heim stores per-user preferences that don't belong to the domain model — things like color scheme, locale, or UI density. Distinct from auth state (session, identity) and from domain state (households, events). + +## Scope + +A "user setting" is a per-user preference that: + +- Affects presentation or client behavior, not domain decisions. +- Is acceptable to lose if a user clears their browser (it's a preference, not data). +- Contains no PII. PII belongs in forgettable payloads, never here. + +First setting: **color scheme**. The same shape applies to future settings like `locale` or `density`. + +## Client storage + +### localStorage keys + +- One key per setting. Key name is a kebab-case noun, preferring the matching CSS or web platform term where one exists. +- Color scheme: key `color-scheme`, values `'light' | 'dark' | 'system'`. Default when unset: `'system'`. +- Future examples (illustrative): `locale` (BCP 47 tag), `density` (`'comfortable' | 'compact'`). + +### DOM exposure + +Settings that affect first render are applied to `` via dataset attributes so CSS can key off them without a paint cycle: + +- `` holds the user's chosen preference, including any `'system'`/auto sentinel. +- `` holds the resolved value, if resolution differs from the raw preference (e.g. `'system'` → `'light'`/`'dark'`). + +For color scheme this means `data-color-scheme-pref` (light/dark/system) and `data-color-scheme` (light/dark). The CSS `color-scheme` property on `` is also set to the resolved value so native UA widgets follow along. + +### Pre-paint IIFE + +Settings that would cause a flash if applied after React mounts must be applied by an inline `