feat(phase 4b): dark/light theme toggle + file-upload permanently out-of-scope
## Slice A — explicit dark/light toggle projax now ships with two palettes and a 1y cookie to remember the choice. Dark is the new default; ☀ button in the header nav flips to light and writes projax_theme=light. Server reads the cookie via themeFromRequest(r) and injects Theme + ThemeColor into every template via the centralised render(w, r, …) path, so first paint never flashes the wrong theme. Inline JS in layout.tmpl handles the toggle without a server roundtrip. Every panel colour now lives in a CSS variable under :root[data-theme=dark|light]; the only hardcoded hex values left are inside those two :root blocks. A future palette tweak is one edit, not 30 selectors. Graph node colours, kind-badges, highlights and warn/ok/bad all have parallel dark/light values picked for contrast. Standalone SVG download bakes the light palette inline because the downloaded asset has no parent :root providing vars — m's existing snapshots stay print-friendly regardless of his current cookie. Login page keeps its embedded dark CSS — it's the gateway, intentionally always dark. Tests: TestThemeDefaultIsDark, TestThemeCookieRoundTrips, TestThemeCookieUnknownFallsBackToDark, TestThemeTogglePagesShareSameTheme, TestThemeToggleScriptPresent, TestThemeColorMetaHelper. Full suite green. ## Slice B — file-upload permanently out of scope (m, 2026-05-17) docs/design.md moves "File uploads / in-projax storage" from the §3c parked list to a permanent "Out of scope (decided 2026-05-17)" clause with the rationale: PER is the cross-reference index, not the file system. docs/standards/per.md gains the same explicit clause so future shifts working from the PER standard see the constraint where they look. Memory note filed so future workers don't re-propose multipart uploads, attachments tables, or documents buckets. ## docs/design.md §13 Theming Documents the toggle approach, cookie semantics, palette table, the standalone-SVG carve-out, the login-page exception, and the 4b out-of-scope (prefers-color-scheme detection, per-page overrides, transitions on swap).
This commit is contained in:
@@ -357,10 +357,13 @@ The PER standard (`docs/standards/per.md`) needs a `(item, event_date)` pair as
|
||||
|
||||
Out of scope (parked):
|
||||
|
||||
- File uploads / in-projax storage. v1 references only.
|
||||
- Recurring dated artifacts (RRULE-style). Flatten for now.
|
||||
- Cross-PER linking syntax / forward-jump anchors. Phase 3d+ if m needs it.
|
||||
|
||||
Out of scope (permanent — decided 2026-05-17):
|
||||
|
||||
- **File uploads / in-projax file storage.** projax `item_links` are *references*, not files. The PER (`{path}.{YYMMDD}`) names where a document lives elsewhere — mFin/, the filesystem, a URL — and the source-of-truth stays there. Do not propose adding multipart uploads, a documents bucket, or an attachments table; m killed that on 2026-05-17. The "documents" surface is intentionally a list of `(ref_type, ref_id, event_date, note)` rows and nothing more.
|
||||
|
||||
## 8. Open questions (post-PRD)
|
||||
|
||||
- **Path-trigger correctness** under cycle attempts: enforce acyclicity via check in trigger.
|
||||
@@ -530,6 +533,46 @@ Items without a date never appear here — the tree/graph/dashboard cover the re
|
||||
- Per-row inline edit of VEVENT (use the source calendar app — the 3l read-only stance still holds for v1).
|
||||
- Gitea issue created/closed activity (deferred until m asks; dashboard already covers it).
|
||||
|
||||
## 13. Theming (Phase 4b)
|
||||
|
||||
projax ships with an explicit dark / light toggle and a 1-year cookie that remembers the choice. Default is dark — m asked for dark-by-default; the toggle exists so light mode is one click away when the ambient lighting calls for it.
|
||||
|
||||
**Toggle approach**:
|
||||
|
||||
- `<html data-theme="dark">` (default) or `<html data-theme="light">`. CSS palettes live under `:root, :root[data-theme="dark"] { … }` and `:root[data-theme="light"] { … }` in `web/static/style.css`. Every panel colour is a `var(--foo)` — the only hardcoded hex values left in the codebase are inside those two `:root` blocks. A future palette tweak edits the variables, not 30 selectors.
|
||||
- The header nav grows a small `☀` / `☾` button (sun glyph in dark mode = "switch to light", moon glyph in light mode = "switch to dark"). Inline JS in `layout.tmpl` flips `data-theme` + the apple `<meta name="theme-color">` and writes the cookie via `document.cookie =` — no server roundtrip on toggle. The cookie is `projax_theme=dark|light`, `Max-Age=31536000`, `Path=/`, `SameSite=Lax`, NOT HttpOnly (the toggle JS reads it).
|
||||
- Layout reads the cookie server-side via `themeFromRequest(r)` and emits `data-theme` + `<meta name="theme-color">` at first paint — no flash-of-wrong-theme before the script runs. The render helper injects `Theme` + `ThemeColor` into every template's data map before execution, so individual handlers don't need to know about the theme.
|
||||
|
||||
**Palette structure**:
|
||||
|
||||
| Variable | Dark | Light | Used for |
|
||||
|---|---|---|---|
|
||||
| `--fg` | `#e6e6e0` | `#1a1a1a` | primary text |
|
||||
| `--muted` | `#8a8880` | `#6a6a6a` | secondary text, slugs |
|
||||
| `--bg` | `#0e0e0e` | `#fafafa` | page background |
|
||||
| `--bg-alt` | `#1a1a1a` | `#f0efe8` | nav bar, code blocks, tag pills |
|
||||
| `--surface` | `#161616` | `#ffffff` | card backgrounds, form inputs |
|
||||
| `--surface-hover` | `#1f1f1f` | `#f7f7f7` | row hover |
|
||||
| `--border` | `#2c2c2c` | `#d8d4c8` | hairline borders |
|
||||
| `--accent` | `#6fa7e8` | `#2f5d9e` | links, primary buttons |
|
||||
| `--accent-fg` | `#0a0a0a` | `#ffffff` | text on accent backgrounds |
|
||||
| `--warn` / `--ok` / `--bad` | brighter pastels | original earthy | status colours |
|
||||
| `--highlight*` | warm dark | cream | PER highlight, banner warn, overdue row |
|
||||
| `--kind-*-bg` / `--kind-*-fg` | dark muted | original pastels | timeline kind-badges |
|
||||
| `--graph-*` | brighter | original | graph node strokes & legend |
|
||||
|
||||
**Theme-color meta** flips with the page theme: `#161616` (dark — matches `--surface`, the nav bar) or `#f0efe8` (light — matches `--bg-alt`). Apple Safari uses this to tint the iOS status bar in the installed PWA.
|
||||
|
||||
**Standalone SVG download** (`/graph?download=svg`) bakes the LIGHT palette inline because the downloaded asset has no parent `:root` providing variables, and m's existing snapshots (presentations, mBrian) expect the original print-friendly look. The `Standalone` flag on `graphPayload` flips a `{{if .Standalone}}` block in `graph_svg.tmpl` to inject the inline `:root` declarations only on the download path.
|
||||
|
||||
**Login page** keeps its embedded dark CSS — it's the gateway and is intentionally always dark (mirrors the lockscreen feel). The theme toggle is hidden from users until they're signed in; switching the login template's palette would only matter for a never-signed-in user.
|
||||
|
||||
**Out of scope for 4b**:
|
||||
|
||||
- `prefers-color-scheme` auto-detect (could add: read it on first visit if no cookie). v1 is manual.
|
||||
- Per-page theme overrides — one global theme is enough.
|
||||
- CSS transitions on the swap. The flip is instant; that's intentional.
|
||||
|
||||
## 9. Phase-1 deliverable checklist
|
||||
|
||||
- [ ] `projax.items` + `projax.item_links` migrations in `db/migrations/`
|
||||
|
||||
@@ -111,6 +111,10 @@ Examples:
|
||||
- **Time-of-day suffixes** — date granularity is enough. If m needs to distinguish "morning meeting" vs "evening call" on the same project on the same day, that's an internal metadata concern, not a PER concern.
|
||||
- **Cross-PER references / linking syntax** — Phase 2+.
|
||||
|
||||
## Out of scope (permanent — decided 2026-05-17)
|
||||
|
||||
- **In-projax file storage / uploads.** A PER *names* a document; it does not contain one. `ref_id` is a pointer (URL, file path, Drive link, mFin location, …) to where the file lives elsewhere. projax never grows a multipart-upload endpoint, an attachments table, or a documents bucket. m killed that direction explicitly on 2026-05-17; do not re-propose it. The source of truth for the bits stays wherever m already keeps it — projax is the cross-reference index, not the file system.
|
||||
|
||||
## Versioning
|
||||
|
||||
PER is a forward-compatible additive format. A v0.2 may add suffixes or qualifiers; v0.1 PERs MUST continue to resolve under any future spec. No backwards-incompatible changes without bumping the major version.
|
||||
|
||||
Reference in New Issue
Block a user