diff --git a/apps/web/src/app/(authenticated)/settings/appearance/page.tsx b/apps/web/src/app/(authenticated)/settings/appearance/page.tsx
new file mode 100644
index 0000000..3149d9a
--- /dev/null
+++ b/apps/web/src/app/(authenticated)/settings/appearance/page.tsx
@@ -0,0 +1,324 @@
+"use client";
+
+import { useState, useCallback } from "react";
+import type { ReactElement } from "react";
+import Link from "next/link";
+import { useTheme } from "@/providers/ThemeProvider";
+import { getAllThemes, type ThemeDefinition } from "@/themes";
+import { apiPatch } from "@/lib/api/client";
+
+function ThemeCard({
+ theme,
+ isActive,
+ onSelect,
+}: {
+ theme: ThemeDefinition;
+ isActive: boolean;
+ onSelect: () => void;
+}): ReactElement {
+ const [hovered, setHovered] = useState(false);
+
+ return (
+
+ );
+}
+
+function SystemThemeCard({
+ isActive,
+ onSelect,
+}: {
+ isActive: boolean;
+ onSelect: () => void;
+}): ReactElement {
+ const [hovered, setHovered] = useState(false);
+
+ return (
+
+ );
+}
+
+export default function AppearanceSettingsPage(): ReactElement {
+ const { theme: preference, setTheme: setLocalTheme } = useTheme();
+ const [saving, setSaving] = useState(false);
+ const allThemes = getAllThemes();
+
+ const handleThemeSelect = useCallback(
+ async (themeId: string) => {
+ setLocalTheme(themeId);
+ setSaving(true);
+ try {
+ await apiPatch("/users/me/preferences", { theme: themeId });
+ } catch {
+ // Theme is still applied locally even if API save fails
+ } finally {
+ setSaving(false);
+ }
+ },
+ [setLocalTheme]
+ );
+
+ return (
+
+ {/* Breadcrumb */}
+
+
+ Settings
+
+ /
+ Appearance
+
+
+ {/* Page header */}
+
+
+ Appearance
+
+
+ Choose a theme for the Mosaic interface
+ {saving && (
+
+ Saving...
+
+ )}
+
+
+
+ {/* Theme grid */}
+
+ {/* System option first */}
+ void handleThemeSelect("system")}
+ />
+
+ {/* All registered themes */}
+ {allThemes.map((t) => (
+ void handleThemeSelect(t.id)}
+ />
+ ))}
+
+
+ );
+}
diff --git a/apps/web/src/app/(authenticated)/settings/page.tsx b/apps/web/src/app/(authenticated)/settings/page.tsx
index facf077..48645c9 100644
--- a/apps/web/src/app/(authenticated)/settings/page.tsx
+++ b/apps/web/src/app/(authenticated)/settings/page.tsx
@@ -95,6 +95,31 @@ function SettingsCategoryCard({ category }: SettingsCategoryCardProps): ReactEle
}
const categories: CategoryConfig[] = [
+ {
+ title: "Appearance",
+ description:
+ "Choose a theme for the interface. Switch between Dark, Light, Nord, Dracula, and more.",
+ href: "/settings/appearance",
+ accent: "var(--ms-pink-500)",
+ iconBg: "rgba(236, 72, 153, 0.12)",
+ icon: (
+
+ ),
+ },
{
title: "Credentials",
description:
diff --git a/docs/TASKS.md b/docs/TASKS.md
index 00ef2eb..425f185 100644
--- a/docs/TASKS.md
+++ b/docs/TASKS.md
@@ -2,33 +2,33 @@
> Single-writer: orchestrator only. Workers read but never modify.
-| id | status | description | issue | repo | branch | depends_on | blocks | agent | started_at | completed_at | estimate | used | notes |
-| ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---- | ---------------------------- | ------------------------------------------------------ | ------------------------------------------- | ------------ | ---------- | ------------ | -------- | ---- | ------------------------------------------ |
-| TW-PLAN-001 | done | Plan MS18 task breakdown, create milestone + issues, populate TASKS.md | — | — | — | | TW-THM-001,TW-WDG-001,TW-EDT-001,TW-KBN-001 | orchestrator | 2026-02-23 | 2026-02-23 | 15K | ~12K | Planning complete, all artifacts committed |
-| TW-THM-001 | done | Theme architecture — Create theme definition interface, theme registry, and 5 built-in themes (Dark, Light, Nord, Dracula, Solarized) as TS files | #487 | web | feat/ms18-theme-architecture | TW-PLAN-001 | TW-THM-002,TW-THM-003 | worker | 2026-02-23 | 2026-02-23 | 30K | ~15K | PR #493 merged |
-| TW-THM-002 | in-progress | ThemeProvider upgrade — Load themes dynamically from registry, apply CSS variables, support instant theme switching without page reload | #487 | web | TBD | TW-THM-001 | TW-THM-003,TW-VER-002 | worker | 2026-02-23 | — | 25K | — | |
-| TW-THM-003 | not-started | Theme selection UI — Settings page section with theme browser, live preview swatches, persist selection to UserPreference.theme via API | #487 | web | TBD | TW-THM-001,TW-THM-002 | TW-VER-002 | worker | — | — | 25K | — | |
-| TW-WDG-001 | not-started | Widget definition seeding — Seed 7 existing widgets into widget_definitions table with correct sizing constraints and configSchema | #488 | api | TBD | TW-PLAN-001 | TW-WDG-002 | worker | — | — | 15K | — | |
-| TW-WDG-002 | not-started | Dashboard → WidgetGrid migration — Replace hardcoded dashboard layout with WidgetGrid, load/save layout via UserLayout API, default layout on first visit | #488 | web | TBD | TW-WDG-001 | TW-WDG-003,TW-WDG-004,TW-WDG-005 | worker | — | — | 40K | — | |
-| TW-WDG-003 | not-started | Widget picker UI — Drawer/dialog to browse available widgets from registry, preview size/description, add to dashboard | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 25K | — | |
-| TW-WDG-004 | not-started | Widget configuration UI — Per-widget settings dialog using configSchema, configure data source/filters/colors/title | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 30K | — | |
-| TW-WDG-005 | not-started | Layout management UI — Save/rename/switch/delete layouts, reset to default. UI controls in dashboard header area | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 20K | — | |
-| TW-EDT-001 | not-started | Tiptap integration — Install @tiptap/react + extensions, build KnowledgeEditor component with toolbar (headings, bold, italic, lists, code, links, tables) | #489 | web | TBD | TW-PLAN-001 | TW-EDT-002 | worker | — | — | 35K | — | |
-| TW-EDT-002 | not-started | Markdown round-trip + File Manager integration — Import markdown to Tiptap, export to markdown + HTML. Replace textarea in knowledge create/edit | #489 | web | TBD | TW-EDT-001 | TW-VER-001 | worker | — | — | 30K | — | |
-| TW-KBN-001 | not-started | Kanban filtering — Add filter bar (project, assignee, priority, search). Support project-level and user-level views. URL param persistence | #490 | web | TBD | TW-PLAN-001 | TW-VER-001 | worker | — | — | 30K | — | |
-| TW-VER-001 | not-started | Tests — Unit tests for new components, update existing tests, fix any regressions | #491 | web | TBD | TW-WDG-003,TW-WDG-004,TW-WDG-005,TW-EDT-002,TW-KBN-001 | TW-VER-002,TW-DOC-001 | worker | — | — | 25K | — | |
-| TW-VER-002 | not-started | Theme verification — Verify all 5 themes render correctly on all pages, no broken colors/contrast issues | #491 | web | TBD | TW-THM-003,TW-VER-001 | TW-DOC-001 | worker | — | — | 15K | — | |
-| TW-DOC-001 | not-started | Documentation updates — TASKS.md, manifest, scratchpad, PRD status updates | #491 | — | — | TW-VER-001,TW-VER-002 | TW-VER-003 | orchestrator | — | — | 10K | — | |
-| TW-VER-003 | not-started | Deploy to Coolify + smoke test — Deploy, verify themes/widgets/editor/kanban all functional, auth working, no console errors | #491 | — | — | TW-DOC-001 | | orchestrator | — | — | 15K | — | |
+| id | status | description | issue | repo | branch | depends_on | blocks | agent | started_at | completed_at | estimate | used | notes |
+| ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---- | -------------------------------- | ------------------------------------------------------ | ------------------------------------------- | ------------ | ---------- | ------------ | -------- | ---- | ------------------------------------------ |
+| TW-PLAN-001 | done | Plan MS18 task breakdown, create milestone + issues, populate TASKS.md | — | — | — | | TW-THM-001,TW-WDG-001,TW-EDT-001,TW-KBN-001 | orchestrator | 2026-02-23 | 2026-02-23 | 15K | ~12K | Planning complete, all artifacts committed |
+| TW-THM-001 | done | Theme architecture — Create theme definition interface, theme registry, and 5 built-in themes (Dark, Light, Nord, Dracula, Solarized) as TS files | #487 | web | feat/ms18-theme-architecture | TW-PLAN-001 | TW-THM-002,TW-THM-003 | worker | 2026-02-23 | 2026-02-23 | 30K | ~15K | PR #493 merged |
+| TW-THM-002 | done | ThemeProvider upgrade — Load themes dynamically from registry, apply CSS variables, support instant theme switching without page reload | #487 | web | feat/ms18-theme-provider-upgrade | TW-THM-001 | TW-THM-003,TW-VER-002 | worker | 2026-02-23 | 2026-02-23 | 25K | ~12K | PR #494 merged |
+| TW-THM-003 | in-progress | Theme selection UI — Settings page section with theme browser, live preview swatches, persist selection to UserPreference.theme via API | #487 | web | feat/ms18-theme-selection-ui | TW-THM-001,TW-THM-002 | TW-VER-002 | worker | 2026-02-23 | — | 25K | — | |
+| TW-WDG-001 | not-started | Widget definition seeding — Seed 7 existing widgets into widget_definitions table with correct sizing constraints and configSchema | #488 | api | TBD | TW-PLAN-001 | TW-WDG-002 | worker | — | — | 15K | — | |
+| TW-WDG-002 | not-started | Dashboard → WidgetGrid migration — Replace hardcoded dashboard layout with WidgetGrid, load/save layout via UserLayout API, default layout on first visit | #488 | web | TBD | TW-WDG-001 | TW-WDG-003,TW-WDG-004,TW-WDG-005 | worker | — | — | 40K | — | |
+| TW-WDG-003 | not-started | Widget picker UI — Drawer/dialog to browse available widgets from registry, preview size/description, add to dashboard | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 25K | — | |
+| TW-WDG-004 | not-started | Widget configuration UI — Per-widget settings dialog using configSchema, configure data source/filters/colors/title | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 30K | — | |
+| TW-WDG-005 | not-started | Layout management UI — Save/rename/switch/delete layouts, reset to default. UI controls in dashboard header area | #488 | web | TBD | TW-WDG-002 | TW-VER-001 | worker | — | — | 20K | — | |
+| TW-EDT-001 | not-started | Tiptap integration — Install @tiptap/react + extensions, build KnowledgeEditor component with toolbar (headings, bold, italic, lists, code, links, tables) | #489 | web | TBD | TW-PLAN-001 | TW-EDT-002 | worker | — | — | 35K | — | |
+| TW-EDT-002 | not-started | Markdown round-trip + File Manager integration — Import markdown to Tiptap, export to markdown + HTML. Replace textarea in knowledge create/edit | #489 | web | TBD | TW-EDT-001 | TW-VER-001 | worker | — | — | 30K | — | |
+| TW-KBN-001 | not-started | Kanban filtering — Add filter bar (project, assignee, priority, search). Support project-level and user-level views. URL param persistence | #490 | web | TBD | TW-PLAN-001 | TW-VER-001 | worker | — | — | 30K | — | |
+| TW-VER-001 | not-started | Tests — Unit tests for new components, update existing tests, fix any regressions | #491 | web | TBD | TW-WDG-003,TW-WDG-004,TW-WDG-005,TW-EDT-002,TW-KBN-001 | TW-VER-002,TW-DOC-001 | worker | — | — | 25K | — | |
+| TW-VER-002 | not-started | Theme verification — Verify all 5 themes render correctly on all pages, no broken colors/contrast issues | #491 | web | TBD | TW-THM-003,TW-VER-001 | TW-DOC-001 | worker | — | — | 15K | — | |
+| TW-DOC-001 | not-started | Documentation updates — TASKS.md, manifest, scratchpad, PRD status updates | #491 | — | — | TW-VER-001,TW-VER-002 | TW-VER-003 | orchestrator | — | — | 10K | — | |
+| TW-VER-003 | not-started | Deploy to Coolify + smoke test — Deploy, verify themes/widgets/editor/kanban all functional, auth working, no console errors | #491 | — | — | TW-DOC-001 | | orchestrator | — | — | 15K | — | |
## Summary
-| Metric | Value |
-| ------------- | --------------------- |
-| Total tasks | 16 |
-| Completed | 2 (PLAN-001, THM-001) |
-| In Progress | 1 (THM-002) |
-| Remaining | 13 |
-| PRs merged | — |
-| Issues closed | — |
-| Milestone | MS18-ThemeWidgets |
+| Metric | Value |
+| ------------- | ------------------------------ |
+| Total tasks | 16 |
+| Completed | 3 (PLAN-001, THM-001, THM-002) |
+| In Progress | 1 (THM-003) |
+| Remaining | 12 |
+| PRs merged | #493, #494 |
+| Issues closed | — |
+| Milestone | MS18-ThemeWidgets |