daybook/issues/017-customizable-document-theme-variants.md
peter a41e8dcfa5 Add issue #017: customizable, switchable document theme variants
Design issue capturing the request to let users customize document-artifact
colors and switch between theme variants, instead of everyone getting one
forced theme. Notes the two separable pieces (structure/skin split via CSS
variables as a prerequisite — the themes currently hardcode colors — and a
switching mechanism), the preset-vs-raw-CSS security tradeoff, dark-mode
interaction, and deck as the concrete trigger. References the existing
planning docs on theme variants/palettes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-03 13:35:32 +01:00

5.5 KiB
Raw Blame History

017 — Customizable, switchable theme variants for document artifacts

Status: Open Type: Feature / design (not a bug)

Motivation

Document artifacts (markdown, html_fragment, deck, and the json/csv viewers) are effectively locked into a single look. The poster's agent can pass a theme (report / tasks / base), but:

  • There's no way for a viewer to switch an artifact's appearance — the theme is fixed at post time and can't even be changed afterward (theme isn't a PATCH-editable field; see ArtifactDetailView.patch).
  • There are only three document themes, differing in more than just color.
  • Recent consistency work (unifying artifact backgrounds to one off-white, issue-driven) further homogenized the look — good for consistency, but it underlines that everyone gets one palette whether they want it or not.

The ask: let people customize the colors for the different document types, keeping the same structural CSS (same elements/selectors, same layout), and provide a way to switch between variants — so the app isn't forcing a single theme on everyone.

Current state (for grounding)

  • theme is a per-artifact CharField, set at create time: theme = data.get("theme") or DEFAULT_THEMES.get(kind, "base") (DEFAULT_THEMES maps markdown/html_fragment→report, deck→deck).
  • Rendered pages link /static/themes/{theme}.css. Theme files: base.css, report.css, tasks.css, deck.css (+ pygments.css).
  • The theme CSS hardcodes colors (e.g. report.css sets background: #faf9f6; color: #1e2b2f directly). There is no color-variable layer, so a new palette today means duplicating a whole theme file rather than swapping a few values.
  • json/csv viewer styling is worse for this: it's inlined into each artifact's stored HTML at create time (wrap_json_viewer/ wrap_csv_viewer), so it can't be restyled after the fact at all without re-rendering.

Two separable pieces

1. Structure/skin separation (prerequisite). Refactor the document themes so structural rules (layout, spacing, element selectors, code highlighting) are defined once, and color lives in CSS custom properties. A "palette" then becomes a small set of variable values, not a duplicated stylesheet. This is the same pattern the app chrome already uses (daybook.css :root + [data-theme="dark"]); the artifact themes just never adopted it. Without this, "customizable colors" means maintaining N near-identical full CSS files.

Note: for json/csv to be re-skinnable, their viewer CSS needs to move from inline-baked to a linked stylesheet (or the palette needs to be linked separately), otherwise only newly-created ones can change.

2. Switching mechanism. Once palettes are swappable, decide who picks and when:

  • Viewer-side switch (like the dark-mode toggle): the person browsing chooses a palette, applied at render/serve time; a personal preference persisted per user. Doesn't change the stored artifact.
  • Per-artifact, editable: make theme/palette a PATCH-editable field so an artifact's look can be changed after posting.
  • Per-kind or global default: a Settings option for "default palette for my markdown artifacts," etc.

These aren't mutually exclusive; a viewer-side personal default is probably the most flexible and matches the "don't force one theme on everyone" framing.

Open questions

  • Preset palettes vs. user-authored CSS. "Customize the CSS" could mean (a) pick from a set of provided palettes, (b) pick colors via a UI that generates a palette, or (c) let users paste raw CSS. (c) is the most flexible but has a real downside: user CSS is an injection surface (less severe than JS — see issue #014 — but still, e.g. CSS can load external resources / exfiltrate via background-image URLs). A curated-palette or color-picker approach (a/b) avoids that; raw-CSS would need the same "trusted content only" caveat as html_full. Recommend starting with presets + maybe a color-picker, not raw CSS.
  • Interaction with app light/dark mode. Artifacts currently always render light regardless of the app's dark mode. Should palettes include dark artifact variants, and should they follow the app theme or be independent?
  • Scope: global vs per-user. TagColor is already global/shared across users by design; would palettes be per-user preferences instead? (Likely yes — a viewing preference, not shared state.)
  • html_full stays out of this entirely (verbatim, poster owns styling).
  • deck is the concrete trigger: it's intentionally dark, and the request to make backgrounds consistent stalled on it precisely because it can't just take the off-white without reworking its text colors — a palette system with proper light/dark deck variants is the real fix.

Suggested phasing

  1. Refactor base/report/tasks/deck (and the json/csv viewers) to drive all colors from CSS variables — no behavior change, just makes everything else possible.
  2. Ship 2–3 curated palettes per document family.
  3. Add a viewer-side palette switch + persisted per-user preference (mirrors the dark-mode toggle plumbing).
  4. Later, optionally: per-artifact editable palette, or a color-picker for custom palettes.

References

  • planning/2026-09-02-theme_variants_and_user_palettes.md — prior design conversation on this exact topic.
  • planning/framework_artifact_rendering.md — rendering/theming framework notes (incl. the "vendor locally, CSS-only" stance and future renderers).

Resolution

(pending)