Skip to content

feat: add custom theme system#103

Merged
ZingerLittleBee merged 30 commits intomainfrom
feature/custom-theme-system
Apr 30, 2026
Merged

feat: add custom theme system#103
ZingerLittleBee merged 30 commits intomainfrom
feature/custom-theme-system

Conversation

@ZingerLittleBee
Copy link
Copy Markdown
Owner

@ZingerLittleBee ZingerLittleBee commented Apr 30, 2026

Summary

  • Add the custom theme data model, validation, reference handling, HTTP APIs, active-theme resolution, and status page theme support.
  • Add dashboard theme management UI with preset/custom theme cards, legacy migration prompt, create/edit/delete/duplicate/import/export flows, OKLCH editing, and scoped public status page rendering.
  • Add bilingual custom theme documentation, manual E2E checklist, and regression coverage for theme UI and migration behavior.

Test Plan

  • cargo fmt
  • cargo clippy --all --benches --tests --examples --all-features
  • cargo test --workspace
  • cd apps/web && bun x ultracite check
  • cd apps/web && bun run test status-pages appearance theme-card delete-theme-dialog themes-new oklch-picker theme-preview theme-editor status
  • cd apps/web && bun run typecheck
  • cd apps/web && bun run build
  • cd apps/docs && bun run build

Notes

  • The web production build still emits the existing TanStack Router warning for src/routes/_authed/servers/index.cells.tsx, which is unrelated to this branch.

Summary by CodeRabbit

Release Notes

  • New Features

    • Custom theme creation and management with light/dark mode variables
    • Theme assignment for individual status pages or following admin default
    • Import/export themes as JSON files
    • Theme duplication and deletion with safety guards
    • Legacy theme migration prompt for existing users
    • Complete theme management API endpoints
  • Documentation

    • Configuration guide for custom themes feature toggle
    • Custom themes user guide (English and Chinese)

Outlines design for user-customizable themes built on the existing
OKLCH CSS variable system. Custom themes are JSON payloads of variable
values, persisted server-side, with separate theme bindings for the
admin dashboard and each public status page. Includes data model,
API contract, frontend editor layout, ThemeProvider refactor, test
strategy, migration/rollback plan, and milestone breakdown.
Apply fixes from spec review:

- Change custom_theme.created_by from INTEGER to TEXT to match the
  actual users.id (String primary key); drop FK to keep audit row
  after user deletion.
- Widen OKLCH validation regex to allow optional alpha channel
  (number or percent), since the existing default theme already
  ships oklch(1 0 0 / 10%) and oklch(1 0 0 / 15%).
- GET /api/settings/active-theme now returns a resolved payload
  including vars_light/vars_dark for custom refs, so the client
  applies the active theme in a single round-trip and the
  first-paint cache stays self-contained.
- localStorage migration no longer auto-PUTs to the server. Member
  browsers stay read-only against the server; admins see a one-time
  prompt on the appearance page to optionally adopt their old
  per-browser color choice as the global active theme.
- Delete-conflict response now uses the existing AppError envelope
  (plain 409 with message). Structured reference list moves to a
  separate GET /api/settings/themes/:id/references endpoint that the
  UI calls before issuing DELETE.
- Replace ConfigService::get_or_default (does not exist) with an
  explicit get(...).await?.unwrap_or_else(...) pattern.
- Fix migration filename format text from m20260YYMM to mYYYYMMDD,
  and correct the "three migrations" wording to two.
- Pin hex<->OKLCH conversion to a maintained color library (culori)
  rather than rolling our own; document fallback if the dependency
  is rejected.
- Add 8.3.1 spelling out where preset variable maps come from
  (handwritten preset-vars.ts plus a vitest invariant test that
  diffs it against the CSS files).
- Extend the feature.custom_themes kill switch with read-time
  coercion so an existing custom-active state degrades to preset
  default without losing the original ref on disk; writes to
  custom:* refs are blocked while disabled.
- Sync the OKLCH validation regex in §7.4 with §5.4 (the residual
  no-alpha pattern would have rejected the same default theme that
  ships with the codebase). Add explicit numeric range checks for
  L/C/H/alpha alongside the regex.
- Replace every `:id` / `:slug` Axum route placeholder with `{id}` /
  `{slug}` to match Axum 0.8 syntax.
- Switch custom_theme.created_at / updated_at from raw INTEGER to
  TIMESTAMP backed by sea-orm DateTimeUtc, and use ISO-8601 strings
  in DTOs to match status_page / incident / maintenance conventions.
- Make the theme: ThemeResolved field a tagged union with both
  custom and preset variants, used by both /api/settings/active-theme
  and /api/status/{slug}; spell out scoped rendering for the preset
  case (data-theme attribute + loadThemeCSS) so the implementation
  does not branch.
- §7.5 explicitly assigns GET /api/settings/themes/{id}/references to
  the admin (write_router) scope despite being a GET, with a full
  route-to-router mapping table to prevent the obvious mistake of
  routing GETs blindly into read_router.
- §3 Brand row: clarify that brand settings live as a configs table
  KV ("brand" key) accessed via ConfigService, not a dedicated table.
- §8.3 culori dependency: require explicit dependency-vetting step
  before adding the package.
- §7.4 OKLCH numeric range checks: pick a single rule per
  component instead of saying "out of range is invalid" right
  after "but C > 0.5 is fine, just lint." L/H/alpha out of
  range -> 422; C has no hard cap (only an in-editor inline
  hint), since high-chroma values are legal OKLCH and only
  get gamut-clipped at render time.
- §7.6 ThemeResolved DTO consistency: add updated_at to the
  custom example so it matches the active-theme example, and
  spell out the discriminated-union field contract once for
  both Rust and TS so the two endpoints really do share one
  DTO.
- §8.4 / §8.8 first-paint cache: rename localStorage key from
  active-theme-ref to active-theme-cache and document that it
  stores the full ActiveThemeResponse JSON. The previous name
  invited the bug of caching only the ref, which would force
  a flash-of-wrong-theme on every reload for custom themes.
Decompose the spec at docs/superpowers/specs/2026-04-30-custom-theme-design.md
into 27 bite-sized tasks across 7 milestones (data layer, API,
frontend foundation, list page, editor, status-page binding, i18n
+ docs + acceptance). Each task includes exact file paths, full
code samples, expected test output, and a commit step. Plan is
designed to be executed via subagent-driven-development or
executing-plans, leaving the build green between tasks.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
server-bee-docs Ready Ready Preview, Comment Apr 30, 2026 4:03pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Implements a complete custom themes feature for ServerBee, allowing administrators to create and manage user-defined light/dark OKLCH-based themes. Includes database schema, backend API endpoints, frontend UI components, theme resolution logic with feature flagging, and comprehensive documentation and tests.

Changes

Cohort / File(s) Summary
Configuration & Documentation
ENV.md, apps/docs/content/docs/*/configuration.mdx, apps/docs/content/docs/*/custom-themes.mdx, apps/docs/content/docs/*/meta.json, crates/server/src/config.rs
Documentation for new SERVERBEE_FEATURE__CUSTOM_THEMES feature flag controlling custom theme usage; specification document detailing architecture and implementation
Database Schema
crates/server/src/entity/custom_theme.rs, crates/server/src/entity/status_page.rs, crates/server/src/migration/m20260430_000019_create_custom_theme.rs, crates/server/src/migration/m20260430_000020_add_status_page_theme_ref.rs, crates/server/src/migration/m20260430_000021_custom_theme_ref_integrity.rs, crates/server/src/migration/mod.rs
New custom_theme table with light/dark variables, creator tracking, and timestamps; status_page.theme_ref column; SQLite triggers enforcing referential integrity for custom theme references
Backend Theme Services
crates/server/src/service/custom_theme.rs, crates/server/src/service/theme_ref.rs, crates/server/src/service/theme_validator.rs, crates/server/src/service/mod.rs
Custom theme CRUD operations with feature gating, variable validation against fixed OKLCH key set with range constraints, theme reference parsing (preset/custom URNs), reference enumeration for safe deletion, active theme resolution with preset/custom discrimination
Backend API Routes
crates/server/src/router/api/theme.rs, crates/server/src/router/api/status_page.rs, crates/server/src/router/api/mod.rs, crates/server/src/openapi.rs
Complete REST endpoints for list/get/create/update/delete/duplicate/export/import/activate theme operations; status page theme resolution with feature flag handling and fallback to default; OpenAPI documentation registration
Frontend Theme Management
apps/web/src/components/theme/theme-card.tsx, apps/web/src/components/theme/theme-editor.tsx, apps/web/src/components/theme/oklch-picker.tsx, apps/web/src/components/theme/theme-preview.tsx, apps/web/src/components/theme/delete-theme-dialog.tsx, apps/web/src/api/themes.ts
React components for theme display (card), editing (editor with light/dark variable separation), OKLCH color selection, live preview with CSS variable binding, deletion confirmation; React Query hooks for theme operations with cache invalidation
Frontend Theme Provider & Routing
apps/web/src/components/theme-provider.tsx, apps/web/src/routes/_authed/settings/appearance.tsx, apps/web/src/routes/_authed/settings/appearance/themes.new.tsx, apps/web/src/routes/_authed/settings/appearance/themes/$id.tsx, apps/web/src/routeTree.gen.ts
Theme provider refactored to derive active theme from API with localStorage caching and cross-tab sync, CSS variable injection for custom themes; appearance page redesigned with preset and custom theme management, legacy color-theme migration detection/prompt; new routes for theme creation and editing
Status Page Theming
apps/web/src/routes/status.$slug.tsx, crates/server/src/router/api/status_page.rs
Public status pages now support custom or preset theme selection via theme_ref field; dynamic CSS injection via data-theme attribute or scoped <style> elements; per-page theme or fallback to active admin theme behavior
Color Utilities
apps/web/src/lib/oklch.ts, apps/web/src/lib/theme-ref.ts, apps/web/package.json
OKLCH color parsing/formatting with alpha handling, hex-to-OKLCH and OKLCH-to-hex conversion via culori; theme reference parsing for preset/custom URNs with validation; adds culori@^4.0.2 dependency
API Schema & Types
apps/web/openapi.json, apps/web/src/lib/api-types.ts, apps/web/src/lib/api-schema.ts, crates/server/src/themes/preset-vars.ts
OpenAPI definitions for 10+ new theme endpoints and schemas (Theme, ThemeResolved, ThemeSummary, CreateThemeInput, UpdateThemeInput, ExportPayload, ActiveThemeResponse); preset variable key mapping with light/dark OKLCH values for all built-in color themes
Localization
apps/web/src/locales/en/settings.json, apps/web/src/locales/en/status.json, apps/web/src/locales/zh/settings.json, apps/web/src/locales/zh/status.json
English and Chinese i18n strings for theme management UI (creation, editing, deletion, import/export), legacy browser theme migration prompts, appearance/status page theme selection labels
Test Coverage
apps/web/src/components/theme/*.test.tsx, apps/web/src/lib/*.test.ts, apps/web/src/themes/preset-vars.test.ts, apps/web/src/routes/_authed/settings/appearance.test.tsx, apps/web/src/routes/_authed/settings/appearance/themes.new.test.tsx, apps/web/src/routes/_authed/settings/status-pages.test.tsx, apps/web/src/routes/status-slug.test.tsx, crates/server/tests/custom_theme_integration.rs, crates/server/tests/email_migration_integration.rs
Component and hook tests for theme card/editor/picker/preview/dialog; OKLCH and theme-ref parsing tests; preset variable synchronization tests; integration tests for admin/member authorization, feature flag behavior, theme deletion constraints; status page payload serialization tests
Test Documentation
tests/README.md, tests/appearance/custom-theme.md
E2E manual validation checklist covering custom theme creation/editing/deletion, preset/custom activation, status page binding, legacy localStorage migration, feature flag toggle behavior, and mobile usability
Minor Formatting & Dependencies
crates/agent/src/config.rs, crates/agent/src/rebind.rs, crates/agent/src/register.rs, crates/agent/src/reporter.rs, crates/common/src/protocol.rs, crates/server/Cargo.toml, apps/web/src/components/server/capabilities-dialog.test.tsx, apps/web/vite.config.ts
Code formatting/line-break adjustments; adds once_cell and regex dependencies to server crate; adds DialogBody mock to test utilities; updates Vite config to ignore .test.ts(x) files in route generation

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Poem

🎨 A custom palette blooms so bright,

With OKLCH hues for dark and light,

Themes dance through queries, refs aligned,

Preset or custom, admin-designed—

Color dreams now saved in SQLite's keep! 🐰

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 15.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title "feat: add custom theme system" directly describes the primary change in the changeset. It is concise, clear, and accurately reflects the main feature addition across both frontend and backend.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/custom-theme-system

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@ZingerLittleBee ZingerLittleBee merged commit 05d28a6 into main Apr 30, 2026
9 of 10 checks passed
@ZingerLittleBee ZingerLittleBee deleted the feature/custom-theme-system branch April 30, 2026 16:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant