From 2c9b4762fc0c54cf9377d533981e12a4024b857d Mon Sep 17 00:00:00 2001 From: Sandro Date: Fri, 30 Oct 2026 23:12:05 +0100 Subject: [PATCH] feat(ui): Terrakotta Nacht replaces Soft, smooth menus and sliding notes on phones - dark counterpart of Terrakotta; Soft users move to it - add tailwindcss-animate: the existing animate-in/fade/slide classes now run; menus zoom in, sheets slide up, notes slide in on phones - no animations with reduced motion chore(release): v0.13.0 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 15 +++- Doku/theming.md | 2 +- README.md | 2 +- frontend/package-lock.json | 15 +++- frontend/package.json | 3 +- frontend/src/App.tsx | 2 +- .../src/features/workspace/DocumentEditor.tsx | 2 +- .../src/features/workspace/JournalView.tsx | 2 +- .../src/features/workspace/WorkspaceView.tsx | 4 +- frontend/src/index.css | 10 ++- frontend/src/lib/__tests__/theme.test.ts | 18 +++++ frontend/src/lib/theme.ts | 22 +++--- frontend/src/themes/theme-soft.css | 67 ------------------ .../src/themes/theme-terracotta-night.css | 69 +++++++++++++++++++ frontend/tailwind.config.js | 4 +- 15 files changed, 148 insertions(+), 89 deletions(-) delete mode 100644 frontend/src/themes/theme-soft.css create mode 100644 frontend/src/themes/theme-terracotta-night.css diff --git a/CHANGELOG.md b/CHANGELOG.md index 931804b..349a9b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.13.0] - 2026-10-30 + +### Added +- Neues Theme **Terrakotta Nacht**: das dunkle Gegenstück zu Terrakotta – Espresso-Braun, Creme-Schrift, glühender Terrakotta-Akzent, Salbeigrün, weiche Ecken. +- Animationen: Menüs (Benutzermenü, Drei-Punkte-Menü), Dialoge und Bottom-Sheets blenden und gleiten weich ein; am Handy schiebt sich eine Notiz von rechts herein und die Liste beim Zurück von links. Am PC bleibt der Notizwechsel sofort. Bei „Bewegung reduzieren“ im System gibt es keine Animationen. + +### Removed +- Theme „Soft Slate“; wer es aktiv hatte, bekommt automatisch „Terrakotta Nacht“. + +### Fixed +- Die vorhandenen Ein-/Ausblend-Animationen (`animate-in`, `fade-in`, `slide-in-from-*`) liefen nie, weil das Tailwind-Plugin fehlte (`tailwindcss-animate`). + ## [0.12.2] - 2026-10-30 ### Fixed @@ -312,7 +324,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V - Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken. - Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung. -[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.2...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.13.0...dev +[0.13.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.2...v0.13.0 [0.12.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.1...v0.12.2 [0.12.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.0...v0.12.1 [0.12.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.11.1...v0.12.0 diff --git a/Doku/theming.md b/Doku/theming.md index f1d18de..1d78744 100644 --- a/Doku/theming.md +++ b/Doku/theming.md @@ -81,7 +81,7 @@ Jede Theme-Datei gliedert sich in sechs exakt definierte Bereiche: | `dark` | [`theme-dark.css`](../frontend/src/themes/theme-dark.css) | Standard Dark Slate / Monochrom | Streng eckig (`0px` Cards, `2px` Buttons) | | `light` | [`theme-light.css`](../frontend/src/themes/theme-light.css) | Daylight Clean White / Minimalistisch | Modern dezent (`4px` Cards, `4px` Buttons) | | `terracotta` | [`theme-terracotta.css`](../frontend/src/themes/theme-terracotta.css) | Warmes Cremepapier, Tinte, Terrakotta-Akzent, eigene Code-Farben in Erdtönen | Weich (`12px`) | -| `soft` | [`theme-soft.css`](../frontend/src/themes/theme-soft.css) | Warmes Soft Slate mit Indigo-Akzent | Weich gerundet (`12px` Cards, `8px` Buttons) | +| `terracotta-night` | [`theme-terracotta-night.css`](../frontend/src/themes/theme-terracotta-night.css) | Espresso-Braun, Creme-Schrift, glühender Terrakotta-Akzent | Weich (`12px`) | | `polarized` | [`theme-polarized.css`](../frontend/src/themes/theme-polarized.css) | Tiefdunkles Obsidian mit elektrischem Grün/Türkis | Streng eckig (`0px` Cards, `2px` Buttons) | | `suri` | [`theme-suri.css`](../frontend/src/themes/theme-suri.css) | Warmes Mitternachts-Pflaume mit samtiger Orchidee | Weich gerundet (`10px` Cards, `6px` Buttons) | | *Template* | [`theme-template.css`](../frontend/src/themes/theme-template.css) | Vollständig dokumentierte Kopiervorlage | Frei anpassbar | diff --git a/README.md b/README.md index ab8bf8e..3780ac8 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Technik dahinter: [Doku/modules/collaboration.md](Doku/modules/collaboration.md) * Geführte Ersteinrichtung, Rollen *Admin* und *Mitglied*, Einladungscodes, optional offene Registrierung. * Gruppen mit Gruppen-Admins für Freigaben. * Admin-Bereich: Benutzer, Einladungen, Instanzname, SMTP, Freigabe-Modus. -* Sechs Themes (Dark, Light, Terrakotta, Soft, Polarized, Suri) plus Theme-Manager für eigene CSS-Themes, installierbar als PWA. Die App startet auch ohne Netz mit dem zuletzt geladenen Stand; offline erfasste Gedanken und abgehakte Aufgaben oder Listeneinträge werden nachgereicht, sobald die Verbindung wieder da ist. +* Sechs Themes (Dark, Light, Terrakotta, Terrakotta Nacht, Polarized, Suri) plus Theme-Manager für eigene CSS-Themes, installierbar als PWA. Die App startet auch ohne Netz mit dem zuletzt geladenen Stand; offline erfasste Gedanken und abgehakte Aufgaben oder Listeneinträge werden nachgereicht, sobald die Verbindung wieder da ist. ### 🔌 Integrationen diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c08030e..c1a6ca3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.12.2", + "version": "0.13.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.12.2", + "version": "0.13.0", "license": "Unlicense", "dependencies": { "@fontsource-variable/inter": "5.3.0", @@ -33,6 +33,7 @@ "jsdom": "^25.0.1", "postcss": "^8.4.49", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "1.0.7", "typescript": "^5.6.3", "typescript-eslint": "8.18.2", "vite": "^5.4.11", @@ -4782,6 +4783,16 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index b5e70e0..fea684d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.12.2", + "version": "0.13.0", "license": "Unlicense", "type": "module", "scripts": { @@ -37,6 +37,7 @@ "jsdom": "^25.0.1", "postcss": "^8.4.49", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "1.0.7", "typescript": "^5.6.3", "typescript-eslint": "8.18.2", "vite": "^5.4.11", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9d20764..f5b7275 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -960,7 +960,7 @@ export const App: React.FC = () => { {showProfileMenu && ( -
+
{/* User Info Header */}
diff --git a/frontend/src/features/workspace/DocumentEditor.tsx b/frontend/src/features/workspace/DocumentEditor.tsx index 7500065..1cd8d55 100644 --- a/frontend/src/features/workspace/DocumentEditor.tsx +++ b/frontend/src/features/workspace/DocumentEditor.tsx @@ -941,7 +941,7 @@ export const DocumentEditor: React.FC = ({ {showActionsMenu && (
{!inTrash && (
{showCalendar && ( -
+
= ({ : mobileShowDetail ? 'hidden lg:flex' : 'flex' - } w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-sidebar border-r border-border transition-all duration-200` + } w-full lg:w-72 xl:w-80 shrink-0 flex-col bg-sidebar border-r border-border transition-all duration-200 max-lg:animate-in max-lg:slide-in-from-left-1/4 max-lg:fade-in max-lg:duration-200` } > {/* Sidebar header: search and the create buttons in one row */} @@ -1925,7 +1925,7 @@ export const WorkspaceView: React.FC = ({
{subView === 'journal' && journalEnabled ? ( { expect(document.documentElement.classList.contains('dark')).toBe(true); }); }); + +describe('built-in themes', () => { + beforeEach(() => localStorage.clear()); + + it('moves removed themes to their successors', () => { + localStorage.setItem('mindsift_active_theme', 'soft'); + expect(getActiveTheme()).toBe('terracotta-night'); + localStorage.setItem('mindsift_active_theme', 'high-contrast'); + expect(getActiveTheme()).toBe('dark'); + }); + + it('treats Terrakotta as a light theme and Terrakotta Nacht as dark', () => { + setActiveTheme('terracotta'); + expect(document.documentElement.classList.contains('dark')).toBe(false); + setActiveTheme('terracotta-night'); + expect(document.documentElement.classList.contains('dark')).toBe(true); + }); +}); diff --git a/frontend/src/lib/theme.ts b/frontend/src/lib/theme.ts index cfd96a6..3b07d79 100644 --- a/frontend/src/lib/theme.ts +++ b/frontend/src/lib/theme.ts @@ -58,14 +58,14 @@ export const AVAILABLE_THEMES: ThemeOption[] = [ isRounded: true, }, { - id: 'soft', - name: 'Soft Slate (Abgerundet)', - subtitle: 'Schiefer-Blau mit weichen Radien (10px)', - file: 'src/themes/theme-soft.css', - previewBg: '#0f172a', - previewPanel: '#1e293b', - previewAccent: '#3b82f6', - previewText: '#f8fafc', + id: 'terracotta-night', + name: 'Terrakotta Nacht (Glut)', + subtitle: 'Espresso, Creme & glühende Terrakotta (12px)', + file: 'src/themes/theme-terracotta-night.css', + previewBg: '#17110d', + previewPanel: '#211913', + previewAccent: '#e8794a', + previewText: '#f3e8da', isRounded: true, }, { @@ -188,8 +188,12 @@ function load(key: string): string | null { } } +// Removed themes and their successors +const RETIRED_THEMES: Record = { soft: 'terracotta-night', 'high-contrast': 'dark' }; + export function getActiveTheme(): ThemeId { - const saved = load(THEME_STORAGE_KEY); + const stored = load(THEME_STORAGE_KEY); + const saved = stored && RETIRED_THEMES[stored] ? RETIRED_THEMES[stored] : stored; if (saved && AVAILABLE_THEMES.some((t) => t.id === saved)) return saved; if (saved && isCustomThemeId(saved) && load(CUSTOM_CSS_CACHE_KEY)) return saved; return 'dark'; diff --git a/frontend/src/themes/theme-soft.css b/frontend/src/themes/theme-soft.css deleted file mode 100644 index 5f6f049..0000000 --- a/frontend/src/themes/theme-soft.css +++ /dev/null @@ -1,67 +0,0 @@ -/* ========================================================================== - MINDSIFT THEME: SOFT SLATE (Abgerundete Geometrie & Sanftes Schiefer-Blau) - Demonstriert, wie über die Rundungs-Variablen in einer einzigen Datei - das gesamte App-Layout auf abgerundete Ecken umschaltet. - ========================================================================== */ - -:root[data-theme="soft"] { - /* --- 1. AKZENTFARBE --- */ - --accent-primary: #3b82f6; - --accent-hover: #60a5fa; - --accent-dim: rgba(59, 130, 246, 0.15); - --accent-border: rgba(96, 165, 250, 0.40); - --text-on-accent: #ffffff; - - /* --- 2. OBERFLÄCHEN & HINTERGRÜNDE --- */ - --bg-app: #0f172a; - --bg-panel: #1e293b; - --bg-panel-elevated: #293548; - --bg-panel-hover: #334155; - --bg-input: #0f172a; - --bg-overlay: rgba(2, 6, 23, 0.72); - --bg-code: #090d16; - - /* --- 3. TEXT & KONTRASTE --- */ - --text-main: #f8fafc; - --text-muted: #94a3b8; - --text-faint: #64748b; - - /* --- 4. RAHMEN, LINIEN & SCHATTEN --- */ - --border-color: rgba(148, 163, 184, 0.16); - --border-subtle: rgba(148, 163, 184, 0.08); - --border-strong: rgba(148, 163, 184, 0.32); - --shadow-panel: 0 12px 32px rgba(2, 6, 23, 0.40); - --scrollbar-thumb: rgba(148, 163, 184, 0.22); - --scrollbar-thumb-hover: rgba(148, 163, 184, 0.38); - - /* --- 5. STATUS- & SIGNALFARBEN --- */ - --color-success: #10b981; - --bg-success: rgba(16, 185, 129, 0.14); - --border-success: rgba(16, 185, 129, 0.38); - - --color-danger: #f43f5e; - --bg-danger: rgba(244, 63, 94, 0.14); - --border-danger: rgba(244, 63, 94, 0.38); - - --color-warning: #f59e0b; - --bg-warning: rgba(245, 158, 11, 0.14); - --border-warning: rgba(245, 158, 11, 0.38); - - /* --- 6. RUNDUNGEN (Weich abgerundete Ecken überall) --- */ - --radius-card: 10px; - --radius-btn: 6px; - --radius-input: 6px; - --radius-checkbox: 4px; - --radius-pill: 9999px; - - /* --- 7. CODE-SYNTAX-HIGHLIGHTING (VS Code Dark+) --- */ - --code-keyword: #569cd6; - --code-string: #ce9178; - --code-number: #b5cea8; - --code-comment: #6a9955; - --code-property: #9cdcfe; - --code-function: #dcdcaa; - --code-type: #4ec9b0; - --code-punctuation: #d4d4d4; - --code-variable: #9cdcfe; -} diff --git a/frontend/src/themes/theme-terracotta-night.css b/frontend/src/themes/theme-terracotta-night.css new file mode 100644 index 0000000..9773bc1 --- /dev/null +++ b/frontend/src/themes/theme-terracotta-night.css @@ -0,0 +1,69 @@ +/* ========================================================================== + MINDSIFT THEME: TERRAKOTTA NACHT (Espresso & Glut) + Das dunkle Gegenstück zu Terrakotta: Espresso-Braun statt Schwarz, + warme Creme-Schrift, glühender Terrakotta-Akzent, Salbeigrün, weiche Ecken. + ========================================================================== */ + +:root[data-theme="terracotta-night"] { + /* --- 1. AKZENTFARBE (Glühende Terrakotta) --- */ + --accent-primary: #e8794a; + --accent-hover: #f39669; + --accent-dim: rgba(232, 121, 74, 0.15); + --accent-border: rgba(232, 121, 74, 0.40); + --text-on-accent: #1c1410; + + /* --- 2. OBERFLÄCHEN & HINTERGRÜNDE (Espresso, Mokka, Leder) --- */ + --bg-app: #17110d; + --bg-panel: #211913; + --bg-panel-elevated: #2c2219; + --bg-panel-hover: #382b20; + --bg-input: #1a130f; + --bg-overlay: rgba(10, 6, 4, 0.72); + --bg-code: #120d0a; + --bg-editor: #1c1510; + --bg-sidebar: #17110d; + + /* --- 3. TEXT & KONTRASTE (Creme auf Espresso) --- */ + --text-main: #f3e8da; + --text-muted: #c4b19c; + --text-faint: #8d7a66; + + /* --- 4. RAHMEN, LINIEN & SCHATTEN --- */ + --border-color: rgba(243, 214, 184, 0.12); + --border-subtle: rgba(243, 214, 184, 0.06); + --border-strong: rgba(243, 214, 184, 0.24); + --shadow-panel: 0 12px 32px rgba(5, 3, 2, 0.55); + --scrollbar-thumb: rgba(232, 121, 74, 0.22); + --scrollbar-thumb-hover: rgba(232, 121, 74, 0.38); + + /* --- 5. STATUS- & SIGNALFARBEN (Salbei, Ziegel, Honig) --- */ + --color-success: #8fbf8a; + --bg-success: rgba(143, 191, 138, 0.14); + --border-success: rgba(143, 191, 138, 0.36); + + --color-danger: #f07a6a; + --bg-danger: rgba(240, 122, 106, 0.14); + --border-danger: rgba(240, 122, 106, 0.36); + + --color-warning: #e6b450; + --bg-warning: rgba(230, 180, 80, 0.14); + --border-warning: rgba(230, 180, 80, 0.36); + + /* --- 6. RUNDUNGEN (Weich wie Papierkarten) --- */ + --radius-card: 12px; + --radius-btn: 8px; + --radius-input: 8px; + --radius-checkbox: 4px; + --radius-pill: 9999px; + + /* --- 7. CODE-SYNTAX-HIGHLIGHTING (warme Erdtöne auf Espresso) --- */ + --code-keyword: #f39669; + --code-string: #a9cf9c; + --code-number: #e6b450; + --code-comment: #8d7a66; + --code-property: #f0c9a4; + --code-function: #d8b4fe; + --code-type: #7fc7b8; + --code-punctuation: #c4b19c; + --code-variable: #f0c9a4; +} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 97fc1fd..ca3a6bc 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -1,3 +1,4 @@ +import animate from 'tailwindcss-animate'; // Theme colours are CSS variables, so Tailwind cannot apply opacity modifiers (bg-accent/15) by itself. // v() mixes the variable with transparent for a modifier; plain classes keep the bare variable, so only // the tints depend on color-mix() support. @@ -98,5 +99,6 @@ export default { }, }, }, - plugins: [], + // animate-in / fade-in / slide-in-from-* / zoom-in-* used by menus, sheets and dialogs + plugins: [animate], };