diff --git a/STATUS.md b/STATUS.md index ec724ce..1e0ae41 100644 --- a/STATUS.md +++ b/STATUS.md @@ -9,7 +9,11 @@ Living progress tracker. Update at the end of each task. The canonical brief is ## Next up -- **03 — Core modules** ([brief](docs/tasks/)). Calendar, lists, notes CRUD. Sequential — must finish 02 first. +- **03 — Drizzle + Postgres setup** ([brief](docs/tasks/03-drizzle-postgres.md)). Phase 1 is sequential through 08. + +## Phase 1 remaining + +- 03 Drizzle + Postgres → 04 module loader → 05 compose/Caddy → 06 Authentik OIDC → 07 household seed → **08 theming infrastructure** (newly added; multi-theme + per-user dark/light, must land before module work so module UIs adopt the token system from day one). ## How to resume in a fresh session diff --git a/docs/tasks/08-theming.md b/docs/tasks/08-theming.md new file mode 100644 index 0000000..f9be264 --- /dev/null +++ b/docs/tasks/08-theming.md @@ -0,0 +1,87 @@ +# 08 — Theming infrastructure (multi-theme + dark mode) + +## Goal + +Set up a CSS-variable-based theme system that supports multiple named themes × `{light, dark}`, persists per user, and switches on the fly without a flash of unstyled content. + +## Why + +Cheap to set up before modules exist, painful to retrofit afterwards. Locking in the structure now means every later component "just works" with theming, and adding a new theme is a CSS block — no code change. + +## Depends on + +- 02 (Next.js skeleton, shadcn already initialized) +- 07 (users table, so we can add columns) + +## Scope + +### Schema additions (new migration in `src/modules/_core/schema.ts`) + +- `users.theme` — text, default `'default'`. +- `users.theme_mode` — text, default `'system'`, check constraint `('light', 'dark', 'system')`. + +### CSS structure (`src/app/globals.css`) + +Use shadcn's CSS-variable conventions, scoped by `data-theme` on ``: + +```css +:root { /* default light tokens */ } +.dark { /* default dark tokens */ } + +[data-theme="warm"] { /* warm light */ } +[data-theme="warm"].dark { /* warm dark */ } +``` + +Ship at least **two** themes (`default` + one more) so the architecture is actually exercised. Token values can be placeholder — refining the palettes is a separate later concern. + +### Theme registry (`src/modules/_core/themes.ts`) + +```ts +export type ThemeMode = "light" | "dark" | "system"; +export type ThemeId = "default" | "warm" | string; + +export const THEMES: ReadonlyArray<{ id: ThemeId; label: string }> = [ + { id: "default", label: "Default" }, + { id: "warm", label: "Warm" }, +]; +``` + +Picker reads from this list — no hardcoded options in the UI. + +### Root layout (server component) + +- Reads the current session; pulls `theme` + `theme_mode`. +- Renders `` on first paint. **No flash.** +- For the `system` mode, the resolved value comes from a small inline `