diff --git a/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html b/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html new file mode 100644 index 0000000..ef53c8d --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/content/ux-analysis-overview.html @@ -0,0 +1,777 @@ + + + + + +Oikos UX/UI Analyse + + + + +
+
UX/UI Analyse · Oikos · April 2026
+

Was funktioniert gut —
und was bremst Oikos aus

+

70 % Mobile-PWA · 30 % Desktop · 11 Module · Vanilla JS · Kein Build-Step

+
+ + +
+
+
+
4
+
Kritische Probleme
(sofort beheben)
+
+
+
5
+
Hohe Priorität
(nächster Sprint)
+
+
+
4
+
Mittlere Priorität
(Backlog)
+
+
+
+ +
+ + +
+
🔴 Kritisch — direkt sichtbare UX-Brüche
+
+ +
+
+
🧭
+
Sidebar 1024–1279 px: Icons ohne Labels oder Tooltips
+
+
+ Bei der häufigsten Desktop-Auflösung zeigt die Sidebar nur Icons — kein Tooltip, kein Label. + Neue Nutzer können die 11 Module nicht erkennen. Das verletzt das Grundprinzip + «nav-label-icon» (HIG/Material). +
+
Keine Discoverability
+
+ +
+
+
👆
+
Modal-Close: 40 px statt 44 px Minimum
+
+
+ .modal-panel__close nutzt --target-md (40 px). + Das iOS-Minimum liegt bei 44 pt. Auf kleinen Displays — gerade beim + Schließen tippend — ist das ein spürbares Frustrationspotenzial. +
+
Apple HIG Violation
+
+ +
+
+
🔗
+
Widget-Links: kein Min-Height-Tap-Target
+
+
+ .widget__link hat 12 px Text, kein explizites min-height. + Der effektive Tippbereich ist ~16–18 px — weit unter 44 px. + Auf dem Dashboard ist dieser Link auf jedem Widget sichtbar. +
+
Tap-Target < 44 px
+
+ +
+
+
🔁
+
Doppelter FAB: .fab vs .page-fab
+
+
+ In layout.css existieren zwei nahezu identische FAB-Klassen (.fab + und .page-fab) mit unterschiedlicher bottom-Berechnung. + Das erzeugt inkonsistente Positionierung auf verschiedenen Seiten. +
+
Inkonsistente UI
+
+ +
+
+ + +
+
↳ Visuell: Navigation-Problem & Lösung
+
+
+
+ + Ist-Zustand: 1024–1279 px (nur Icons) +
+
+
+
+
+
+
+
+
+
+
+

Welcher Icon ist "Geburtstage"?

+
+
+
+
+ + Soll-Zustand: Tooltip bei Hover (min) +
+
+
+
+
+
Dashboard
+
+
+
+
+
+
+

Sofort klar durch title-Tooltip

+
+
+
+
+ + +
+
↳ Visuell: Touch-Target Größen
+
+
+
+ + Ist-Zustand: Modal-Close 40 × 40 px +
+
+
+
+
+
40 × 40 px — 4 px unter iOS-Minimum
+
+
+
+ Fingertipp ~44–50 px → Fehlklick-Rate steigt +
+
+
+
+
+
+
+ + Fix: --target-base (44 px) +
+
+
+
+
+
44 × 44 px — Apple HIG Minimum
+
+
+
+ Einzeiler-Fix: --target-base statt --target-md +
+
+
+
+
+
+
+ +
+ + +
+
🟡 Hohe Priorität — spürbare UX-Einschränkungen
+
+ +
+
+
🗂️
+
Onboarding: 3 Schritte für 11 Module
+
+
+ Neue Nutzer sehen einmalig 3 generische Onboarding-Screens. + Module wie Budget, RRule-Wiederholungen oder Google Calendar-Sync + werden nie erklärt. Kein Feature-Discovery-Mechanismus danach. +
+
Discoverability
+
+ +
+
+
📊
+
Dashboard-Widgets: keine Reihenfolge-Anpassung
+
+
+ Widgets können ein-/ausgeblendet werden, aber nicht umsortiert. + Die Widget-Config speichert nur { id, visible } — kein + Reihenfolge-Feld. Für Familien mit verschiedenen Prioritäten ist das stark limitierend. +
+
Personalisierung
+
+ +
+
+
↩️
+
Inkonsistentes Undo-Verhalten
+
+
+ Einige Aktionen zeigen einen Undo-Toast, andere nicht. + Es gibt kein zentrales Undo-System. Bei destruktiven Aktionen + (z. B. Kontakt löschen) fehlt der Undo-Pfad komplett. +
+
Fehlererholung
+
+ +
+
+
📡
+
Kein sichtbarer Offline-Indikator
+
+
+ Der Service Worker existiert, aber der App-Shell fehlt ein + Offline-Banner. Nutzer bemerken den Offline-Zustand erst, + wenn eine API-Anfrage fehlschlägt — zu spät. +
+
PWA-Erfahrung
+
+ +
+
+
⌨️
+
Desktop: keine Keyboard Shortcuts
+
+
+ Bei 30 % Desktop-Nutzung fehlen globale Shortcuts. + Kein „N" für neue Aufgabe, kein „/" für Suche, kein + Escape-Verhalten im globalem Kontext. Power-User müssen + alles mit der Maus bedienen. +
+
Desktop-Ergonomie
+
+ +
+
+ +
+ + +
+
🟢 Mittlere Priorität — qualitative Verbesserungen
+
+ +
+
+
💬
+
reminders.css global geladen
+
+
+ reminders.css wird laut Observations global geladen, + nicht lazy. Auf Seiten ohne Reminder-UI werden unnötige Styles + geparst. Kein Blocking-Problem, aber vermeidbare CSS-Last. +
+
Performance
+
+ +
+
+
🃏
+
Swipe-Geste: nur Tasks & Shopping
+
+
+ Swipe-Reveal ist für Tasks und Shopping implementiert, + fehlt aber bei Kontakten, Notizen und Geburtstagen — obwohl + die Interaktion dort genauso wertvoll wäre. + Inkonsistente Erwartungshaltung. +
+
Interaktions-Konsistenz
+
+ +
+
+
🎨
+
11 Modulfarben gleichzeitig im Dashboard
+
+
+ Wenn alle Widgets sichtbar sind, treffen 11 verschiedene + Akzentfarben aufeinander. Das Dashboard wirkt farblich + überladen. Weniger Kontrast zwischen den Modulen würde + die Ruhewirkung verbessern. +
+
Visuelle Ruhe
+
+ +
+
+
📱
+
Toast: kein Swipe-to-Dismiss auf Mobile
+
+
+ Toasts können nicht weggewischt werden — nur auto-dismiss + oder Undo-Button. Auf Mobile ist Swipe-to-Dismiss eine + etablierte Konvention (iOS, Android), deren Fehlen auffällt. +
+
Mobile-Konvention
+
+ +
+
+ +
+ + +
+
✨ Was bereits ausgezeichnet ist
+
+
+
🌙 Dark Mode
+
Private-/Public-Token-Architektur ist mustergültig. Alle Kontrastverhältnisse WCAG-geprüft, Toast-Texte passen sich an.
+
+
+
♿ Accessibility-Schichten
+
prefers-reduced-motion, prefers-reduced-transparency, prefers-contrast, forced-colors — alle implementiert. Selten in selbstgehosteten Apps.
+
+
+
🍎 iOS-PWA-Bewusstsein
+
100dvh + -webkit-fill-available Fallback, safe-area-inset-*, Flex-Kind statt fixed für Bottom-Nav — solide PWA-Grundlage.
+
+
+
💎 Glass Design System
+
@supports-basiert mit korrekten webkit-Fallbacks, opake Fallbacks für reduced-transparency. Konsistente Token-Hierarchie.
+
+
+
🎭 Modul-Theming
+
--active-module-accent + --module-accent System ist elegant. FAB, Nav-Item und Toggles reflektieren automatisch die aktive Seite.
+
+
+
📐 Design Tokens
+
Vollständige Skala für Farben, Radien, Schatten, Spacing, Typografie. Konsistente Anwendung — kaum Hardcoding gefunden.
+
+
+
+ + + + diff --git a/.superpowers/brainstorm/947899-1777303942/state/server-stopped b/.superpowers/brainstorm/947899-1777303942/state/server-stopped new file mode 100644 index 0000000..6058001 --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/state/server-stopped @@ -0,0 +1 @@ +{"reason":"idle timeout","timestamp":1777305983821} diff --git a/.superpowers/brainstorm/947899-1777303942/state/server.pid b/.superpowers/brainstorm/947899-1777303942/state/server.pid new file mode 100644 index 0000000..47b52d0 --- /dev/null +++ b/.superpowers/brainstorm/947899-1777303942/state/server.pid @@ -0,0 +1 @@ +947907 diff --git a/.superpowers/brainstorm/958518-1777309033/content/01-overview.html b/.superpowers/brainstorm/958518-1777309033/content/01-overview.html new file mode 100644 index 0000000..8db331e --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/content/01-overview.html @@ -0,0 +1,179 @@ + + + + + +Oikos UX/UI Analyse + + + +
+
Oikos UX/UI Analyse · April 2026
+

Was funktioniert — was bremst Oikos

+

70 % Mobile-PWA · 30 % Desktop · 11 Module · Vanilla JS

+
+ +
+
4
Kritisch
sofort beheben
+
5
Hohe Priorität
nächster Sprint
+
4
Mittlere Priorität
Backlog
+
+ +
+ +
+
🔴 Kritisch
+
+
+
🧭
+
Sidebar 1024–1279 px: Icons ohne Labels/Tooltips
+
Bei häufigster Desktop-Auflösung sind Modul-Icons ohne jede Beschriftung — kein Tooltip, kein Label. 11 Module sind nicht erkennbar.
+
Keine Discoverability
+
+
+
👆
+
Modal-Close: 40 px statt 44 px Minimum
+
.modal-panel__close nutzt --target-md (40 px). iOS-Minimum ist 44 pt. Auf kleinen Screens spürbares Frustrationspotenzial.
+
Apple HIG Violation
+
+
+
🔗
+
Widget-Links: Tap-Target < 44 px
+
.widget__link: 12 px Text, kein min-height. Effektiver Tippbereich ~16–18 px. Auf jedem Dashboard-Widget vorhanden.
+
Tap-Target Violation
+
+
+
🔁
+
Doppelter FAB: .fab vs .page-fab
+
Zwei nahezu identische FAB-Klassen mit unterschiedlicher bottom-Berechnung erzeugen inkonsistente Positionierung auf verschiedenen Seiten.
+
Inkonsistente UI
+
+
+
+ +
+
🟡 Hoch
+
+
+
🗂️
+
Onboarding: 3 Screens für 11 Module
+
3 generische Steps erklären nicht Budget, RRule-Wiederholungen oder Calendar-Sync. Kein Feature-Discovery danach.
+
Discoverability
+
+
+
📊
+
Widget-Reihenfolge nicht anpassbar
+
Config speichert nur { id, visible } — kein order-Feld. Familien mit verschiedenen Prioritäten können das Dashboard nicht sinnvoll anpassen.
+
Personalisierung
+
+
+
↩️
+
Inkonsistentes Undo
+
Manche Aktionen zeigen Undo-Toast, andere nicht. Kein zentrales Undo-System. Bei Kontakt/Notiz löschen fehlt der Weg zurück.
+
Fehlererholung
+
+
+
📡
+
Kein sichtbarer Offline-Indikator
+
Service Worker existiert, aber kein Offline-Banner in der App-Shell. Nutzer merken Offline erst beim API-Fehler — zu spät.
+
PWA-Erfahrung
+
+
+
⌨️
+
Keine Keyboard Shortcuts
+
30 % Desktop-Nutzung ohne globale Shortcuts. Kein „N" für neu, kein „/" für Suche. Power-User auf Maus angewiesen.
+
Desktop-Ergonomie
+
+
+
+ +
+
🟢 Mittel
+
+
+
📦
+
reminders.css global geladen
+
Wird auf allen Seiten geparst, nicht lazy. Kein Blocking-Problem, aber vermeidbare CSS-Last auf Seiten ohne Reminder-UI.
+
Performance
+
+
+
👉
+
Swipe nur Tasks & Shopping
+
Swipe-Reveal fehlt bei Kontakten, Notizen, Geburtstagen. Inkonsistente Erwartungshaltung im selben App-Kontext.
+
Interaktions-Konsistenz
+
+
+
🎨
+
11 Modulfarben gleichzeitig im Dashboard
+
Wenn alle Widgets sichtbar sind, treffen 11 Akzentfarben aufeinander. Das Dashboard kann farblich überladen wirken.
+
Visuelle Ruhe
+
+
+
💬
+
Toast: kein Swipe-to-Dismiss
+
Toasts sind nicht wegwischbar. Auf iOS/Android ist Swipe-to-Dismiss eine fest etablierte Konvention — ihr Fehlen fällt auf.
+
Mobile-Konvention
+
+
+
+ +
+ +
+
✨ Was bereits ausgezeichnet ist
+
+
🌙 Dark Mode Architektur
Private/Public-Token-Pattern ist mustergültig. Alle Kontraste WCAG-geprüft.
+
♿ 4 A11y-Schichten
reduced-motion, reduced-transparency, prefers-contrast, forced-colors — alle implementiert.
+
🍎 iOS-PWA-Bewusstsein
100dvh + webkit-Fallback, safe-area-inset, Flex-Kind statt fixed nav.
+
💎 Glass Design System
@supports-basiert, opake Fallbacks für reduced-transparency, konsistente Token.
+
🎭 Modul-Theming
--active-module-accent System elegant — FAB, Nav, Toggles spiegeln aktive Seite.
+
📐 Design Tokens
Vollständige Skala, kaum Hardcoding. Basis für alles weitere.
+
+
+ + + diff --git a/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html b/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html new file mode 100644 index 0000000..49d0938 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/content/02-solutions.html @@ -0,0 +1,629 @@ + + + + + +Oikos — Lösungsvorschläge + + + + +
+
Lösungsvorschläge · Oikos · April 2026
+

Konkrete Optimierungen — sortiert nach Impact

+

13 Punkte · 4 kritisch · 5 hoch · 4 mittel

+
+ + + + +
+
+
1
+
+
Kritisch · 1–2 h
+
Sidebar: title-Tooltips für icon-only Modus
+
Nutzer bei 1024–1279 px sehen 11 Icons ohne jede Beschriftung. Ein einfaches title-Attribut auf jedem Nav-Item genügt als sofortiger Fix. Langfristig: expanded sidebar ab 1280 px früher aktivieren.
+
+
+
+
+
Ist-Zustand
+
+
+
+
+
+
+
+
+

Kein Hinweis
was die Icons bedeuten

+
+
+
+
Soll-Zustand
+
+
+
+
+
Dashboard
+
+
+
+
+
+
+

Tooltip bei
Hover/Focus sichtbar

+
+
+
+
+
router.js — Nav-Item Rendering+1 Zeile
+
+// In renderAppShell() oder wo nav-items erzeugt werden: +- a.setAttribute('aria-label', label); ++ a.setAttribute('aria-label', label); ++ a.setAttribute('title', label); // Tooltip für collapsed sidebar +
+/* Optional: CSS-Tooltip statt native title (für Styling) */ ++ .nav-sidebar .nav-item[title]:hover::after { ++ content: attr(title); ++ position: absolute; ++ left: calc(100% + 10px); ++ /* ... Tooltip-Styles */ ++ } +
+
+
+ +
+ + +
+
+
2
+
+
Kritisch · 5 min
+
Modal-Close: von 40 px auf 44 px
+
Ein Einzeiler in layout.css. Der Fehler liegt darin, dass --target-md (40 px) statt --target-base (44 px) verwendet wird. Das Token existiert bereits — es muss nur gewechselt werden.
+
+
+
+
+
Problem: 40 × 40 px
+
+
+
+
+
40 × 40 px–target-md
4 px unter Minimum
+
+
+ Ein durchschnittlicher Fingertipp belegt 44–50 px. Beim Schließen unter Stress (mit einer Hand, unterwegs) erhöht sich die Fehlklickrate spürbar. +
+
+
+
+
+
Fix: 44 × 44 px
+
+
+
+
+
44 × 44 px--target-base
Apple HIG ✓
+
+
+
+
+
+
+
layout.css · .modal-panel__close1 Zeile
+
+- width: var(--target-md); /* 40px */ +- height: var(--target-md); /* 40px */ ++ width: var(--target-base); /* 44px — Apple HIG Minimum */ ++ height: var(--target-base); /* 44px */ +
+
+
+ +
+ + +
+
+
3
+
+
Kritisch · 15 min
+
Widget-Links: Tap-Target auf 44 px bringen
+
Der „Alle anzeigen →"-Link in jedem Dashboard-Widget hat keinen definierten min-height. Bei 12 px Text liegt der tatsächliche Klickbereich bei etwa 16–18 px — weit unter iOS-Minimum. Fix: padding + min-height in dashboard.css.
+
+
+
+
+
Ist-Zustand
+
+
+
+
Aufgaben
+ Alle → +
+
+
+

Tap-Target ≈ 16 px Höhe

+
+
+
+
Soll-Zustand
+
+
+
+
Aufgaben
+ Alle → +
+
+
+

Tap-Target ≥ 32 px mit Padding

+
+
+
+
+
dashboard.css · .widget__link
+
++ .widget__link { ++ min-height: var(--target-base); /* 44px */ ++ display: inline-flex; ++ align-items: center; ++ padding: 0 var(--space-2); ++ border-radius: var(--radius-sm); ++ } ++ .widget__link:hover { ++ background: color-mix(in srgb, var(--widget-accent) 10%, transparent); ++ } +
+
+
+ +
+ + +
+
+
4
+
+
Kritisch · 30 min
+
FAB konsolidieren: .fab + .page-fab → eine Klasse
+
Beide Klassen definieren fast denselben Button mit leicht abweichender bottom-Berechnung. Das führt zu inkonsistenter Positionierung. Die Lösung: eine Klasse, ein Token, alle Seiten konsistent.
+
+
+
+
+
Ist-Zustand: 2 Klassen
+
+
+
+
+
+
.fab
+
bottom: nav-height + safe-area + space-4
+
+
+
+
+
+
.page-fab
+
bottom: nav-bottom-height + 24px + safe-area
+
+
+
+

Verschiedene bottom-Werte → visuell inkonsistent je nach Seite

+
+
+
+
Soll-Zustand: 1 Klasse
+
+
+
+
+
+
.page-fab (Canonical)
+
Einheitliche bottom-Formel, alle Seiten
+
+
+
+

Alle Seiten nutzen .page-fab, .fab wird entfernt oder als Alias gesetzt

+
+
+
+
+ +
+ + + +
+
+
5
+
+
Hoch · 2–4 h
+
Dashboard: Widget-Reihenfolge anpassbar machen
+
Die Widget-Config speichert aktuell nur { id, visible }. Ein order-Feld hinzufügen und im Customization-Modal drag-sortierbar machen (per touch-action: none + pointer events, kein externes Drag-Library nötig).
+
+
+
+
+
Ist-Zustand: Feste Reihenfolge
+
+
+
+
+
Dashboard
+
⊙ sichtbar
+
+
+
+
Aufgaben
+
⊙ sichtbar
+
+
+
+
Kalender
+
⊙ sichtbar
+
+
+

Reihenfolge fest codiert — nicht änderbar

+
+
+
+
Soll-Zustand: Drag-to-reorder
+
+
+
+
+
+
Dashboard
+
+
+
+
+
+
Kalender
+
+
+
+
+
+
Aufgaben
+
+
+
+

⠿ Handle zum Sortieren — gespeichert in localStorage

+
+
+
+
+
dashboard.js — Config-Schema
+
+- const DEFAULT_WIDGET_CONFIG = WIDGET_IDS.map((id) => ({ id, visible: true })); ++ const DEFAULT_WIDGET_CONFIG = WIDGET_IDS.map((id, i) => ({ id, visible: true, order: i })); +
+// Beim Laden: nach order sortieren ++ config.sort((a, b) => a.order - b.order); +
+
+
+ +
+ + +
+
+
6
+
+
Hoch · 1 h
+
Offline-Banner in App-Shell
+
Der Service Worker ist vorhanden, aber die App gibt kein visuelles Feedback zum Offline-Zustand. Ein kleines Banner direkt unter der Navigation (wenn navigator.onLine false ist) ist die robusteste Lösung — kein Flickering, immer sichtbar.
+
+
+
+
Soll-Zustand: Shell-Level Banner
+
+
+ 📡 + Offline — Änderungen werden gespeichert und beim nächsten Verbindungsaufbau synchronisiert. + Offline +
+

Erscheint unter Nav-Bar, verschwindet automatisch wenn online

+
+
+
+
router.js — App-Shell Setup
+
++ function initOfflineBanner() { ++ const banner = document.getElementById('offline-banner'); ++ const update = () => banner.hidden = navigator.onLine; ++ window.addEventListener('online', update); ++ window.addEventListener('offline', update); ++ update(); ++ } +
+
+
+ +
+ + +
+
+
7
+
+
Hoch · 2–3 h
+
Globale Keyboard Shortcuts (Desktop)
+
30 % der Nutzer verwenden Desktop. Ein zentrales Keyboard-Shortcut-System im Router beschleunigt häufige Aktionen erheblich. Alle Shortcuts per ? einsehbar.
+
+
+
+
/Suche öffnen
+
NNeu (kontextabhängig)
+
G D→ Dashboard
+
G T→ Tasks
+
G C→ Kalender
+
EscModal / Sheet schließen
+
?Shortcut-Übersicht
+
⌘KCommand Palette
+
+
+ +
+ + +
+
+
8
+
+
Hoch · 3–4 h
+
Zentrales Undo-System für destruktive Aktionen
+
Aktuell haben manche Aktionen Undo-Toasts, andere nicht. Eine zentrale undoStack-Utility mit standardisiertem Toast-Muster schafft konsistente Sicherheit über alle Module hinweg.
+
+
+
+
Soll-Zustand: konsistente Undo-Abdeckung
+
+
+
Aufgabe löschen
✓ Undo vorhanden
+
Eintrag Einkaufsliste löschen
✓ Undo vorhanden
+
Kontakt löschen
✗ fehlt
+
Notiz löschen
✗ fehlt
+
Geburtstag löschen
✗ fehlt
+
Mahlzeit löschen
✗ fehlt
+
+

→ Alle DELETE-Aktionen über eine zentrale deleteWithUndo(url, label) Funktion routen

+
+
+
+ +
+ + +
+
+
9
+
+
Hoch · 3 h
+
Onboarding: Modul-spezifische Erst-Nutzung
+
Statt 3 generischen Screens beim ersten Start: leere Zustände auf jeder Seite mit einem konkreten Tipp für genau dieses Modul. Zusätzlich: ein permanentes „?" / Hilfe-Icon in der Toolbar für den Onboarding-Replay.
+
+
+
+
+
📋
+
Aufgaben
+
Tippe + um deine erste Aufgabe zu erstellen. Wische links zum Löschen.
+
+
+
+
📅
+
Kalender
+
Verbinde deinen Google Kalender unter Einstellungen → Kalender-Sync.
+
+
+
+
💰
+
Budget
+
Lege Kategorien und ein Monatsbudget fest. Einnahmen und Ausgaben werden automatisch summiert.
+
+
+
+
🔔
+
Erinnerungen
+
RRule ermöglicht Wiederholungen: täglich, wöchentlich, oder komplex wie „jeden 2. Montag".
+
+
+
+
+7 weitere
+
Jedes Modul erklärt sich beim ersten Besuch selbst.
+
+
+
+ +
+ + + +
+
+
10–13
+
+
Mittel · Backlog
+
Weitere Optimierungen
+
+
+
+
+
🟢 Toast Swipe-to-Dismiss
+
+

+ pointerdown + pointermove auf .toast → bei + >40 px horizontaler Bewegung toast--out Klasse setzen und entfernen. + Entspricht iOS/Android-Konvention. Ca. 30 Zeilen JS. +

+
+
+
+
🟢 Swipe-Reveal auf alle Listen
+
+

+ Das bestehende .swipe-row Pattern auf Kontakte, Notizen und Geburtstage + ausweiten. Die Basis-CSS existiert bereits in layout.css — + nur modul-spezifische Reveal-Farben und JS-Handler ergänzen. +

+
+
+
+
🟢 Dashboard: weniger Farbrauschen
+
+

+ Widget-Icons auf neutrale --color-text-secondary setzen, Widget-Akzentfarbe + nur für Badge und Link reservieren. Reduziert visuelle Überforderung wenn alle + 11 Widgets gleichzeitig sichtbar sind. +

+
+
+
+
🟢 reminders.css lazy laden
+
+

+ reminders.css dynamisch per <link rel="stylesheet"> + nur in den Seiten laden, die Reminders anzeigen — analog zum bestehenden + Lazy-Loading-Pattern für Page-Module. +

+
+
+
+
+ + + diff --git a/.superpowers/brainstorm/958518-1777309033/state/server-stopped b/.superpowers/brainstorm/958518-1777309033/state/server-stopped new file mode 100644 index 0000000..7870285 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/state/server-stopped @@ -0,0 +1 @@ +{"reason":"idle timeout","timestamp":1777311074842} diff --git a/.superpowers/brainstorm/958518-1777309033/state/server.pid b/.superpowers/brainstorm/958518-1777309033/state/server.pid new file mode 100644 index 0000000..1b0c8d1 --- /dev/null +++ b/.superpowers/brainstorm/958518-1777309033/state/server.pid @@ -0,0 +1 @@ +958547 diff --git a/public/pages/birthdays.js b/public/pages/birthdays.js index 2d39140..aa99607 100644 --- a/public/pages/birthdays.js +++ b/public/pages/birthdays.js @@ -20,6 +20,50 @@ function initials(name) { .join('') || '?'; } +const REMINDER_OFFSETS = () => [ + { value: '', label: t('reminders.offsetNone') }, + { value: '0', label: t('reminders.offsetAtTime') }, + { value: '15', label: t('reminders.offset15min') }, + { value: '60', label: t('reminders.offset1hour') }, + { value: '1440', label: t('reminders.offset1day') }, + { value: '2880', label: t('reminders.offset2days') }, + { value: '10080', label: t('reminders.offset1week') }, + { value: '20160', label: t('reminders.offset2weeks') }, + { value: 'custom', label: t('reminders.offsetCustom') }, +]; + +function renderBirthdayReminderSection(birthday = null) { + const currentOffset = birthday?.reminder_offset ?? '0'; + const customAmount = birthday?.reminder_custom_amount || 1; + const customUnit = birthday?.reminder_custom_unit || 'days'; + return ` +
+
+ + +
+ +
`; +} + function ageNote(birthday) { if (birthday.days_until === 0) return t('birthdays.ageNoteToday', { age: birthday.next_age }); if (birthday.days_until === 1) return t('birthdays.ageNoteTomorrow', { age: birthday.next_age }); @@ -327,6 +371,7 @@ function openBirthdayModal({ mode, birthday = null }) { + ${renderBirthdayReminderSection(birthday)}
${t('birthdays.calendarHint')}