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 `