Theme Creator — Free-Form Supporter Color Customization
Status: Shipped — July 2026 (PR #1796) Author: Penny (proposer agent) Date: 2026-07-25 Roadmap ref: none — single bundled phase, no milestone (Josh confirmed) PRD file: docs/product/completed/supporter-theme-creator-prd.md
1. Problem
The Supporter tier's flagship perk ("custom app themes," docs/product/pricing-philosophy.md) shipped as 9 fixed, hand-authored color palettes (ionic_frontend/src/config/themes.ts, ionic_frontend/src/theme/supporter-themes.css). Supporters chose from a curated list; they couldn't express their own color preference. docs/features/supporter.md recorded this as a known, unscoped gap ("Theme creator — Let Supporters customize primary/accent colors freely," Priority: Low).
Objectuve's competitive analysis rated Objectuve "Moderate" on Customization & Flexibility versus Habitica's "Advanced" (docs/product/competitive/feature-matrix.md §11), attributable in part to fixed-palette-only theming versus deeper cosmetic personalization elsewhere in the category.
2. Goal
Let Supporters pick their own primary and accent colors, while guaranteeing every resulting combination stays WCAG AA-compliant and visually coherent with the rest of the design system.
3. Non-goals
- Not a redesign of the base design token system (
design_system/) — extends the existing Supporter-only override layer (html[data-theme="x"]), doesn't replace it. - Not a functional/free-tier feature — stays Supporter-gated per
docs/product/pricing-philosophy.md. - Not full per-component theming (fonts, spacing, iconography) — color only.
- Not a marketplace or sharing mechanism.
4. What shipped
- Picker UX — constrained sliders (Primary hue, Accent hue, Depth/saturation), not raw hex —
ThemeCreator.vue. Lightness is never user-controlled; it's always seeded and left to the AA clamp. - Contrast-safe generation —
deriveCustomTheme(ionic_frontend/src/lib/customTheme.ts) derives the full ~15-variable token set from a picked primary/accent hue, clamping lightness until every WCAG AA condition holds (4.5:1 text, 3:1 large-text/UI) in both light and dark. A pick that would fail AA is auto-adjusted with visible live feedback ("Nudged for readability."), never silently accepted or hard-rejected. - Persistence — localStorage-only in this phase (payload shape — hues, not a slug — designed so a future backend field can carry it verbatim for cross-device sync, coordinated with the OBJ-1757 preset-sync work). Cross-device sync of the actual custom payload remains a follow-up, tracked separately.
- Live preview — reuses
ThemePreviewTile.vue, always rendering the clamped result. - Reset — one-tap return to the Supporter's last-selected preset, persisted so it survives a reload.
Full architecture, file map, and test coverage: docs/features/supporter.md — Theme Creator.
5. Decisions locked during planning (Orion)
- Constrained hue-wheel-style sliders, not freeform hex.
- A 10th "Custom" tile appended to the existing preset grid (kept all 9 presets); grid is 11 tiles total including the free "Legacy Blue" default.
- No interaction with the unrelated Critical Path mini-game theme system (
stimThemes.ts) — confirmed fully separate. - Theme-selection analytics deferred (none exist today for presets or custom).
6. Success metrics (not yet instrumented)
- % of Supporters who create a custom theme within 30 days of shipping
- Supporter tier conversion rate delta (one of several concurrent variables, won't isolate cleanly)
- Zero WCAG AA contrast regressions shipped (CI-gated via
tests/unit/tokenDrift.spec.ts+tests/unit/lib/customTheme.test.ts)
No adoption analytics are wired up yet — this PRD's own scoping deferred instrumentation to a follow-up.
7. Shipping notes
PR #1790 (the original implementation) merged to master and was emergency-reverted 8 minutes later (PR #1795) after it accidentally bundled in the entire unmerged gsd/v4.14-activity-nexus-connected-apps branch, breaking the staging deploy pipeline. The Theme Creator work itself was unaffected — it was re-cut cleanly off master and shipped via PR #1796. See CHANGELOG.md's ci: PR-scope guard against cross-branch contamination entry for the incident writeup and the new CI guard that now catches this class of bundling.
8. Related documents
- Pricing Philosophy — Supporter perk rules
- Feature Matrix — Customization & Flexibility comparison (§11)
docs/features/supporter.md— theming architecture, including the Theme Creator section
Last updated: 2026-07-25