fix(ux): improve microinteractions across the app

1. Nav-item tap: smooth scale transition instead of abrupt snap
2. Custom toggle switch: iOS-style toggle replaces native checkboxes
3. Focus-visible: outline on cards, buttons, FABs for keyboard users
4. Empty-state: gentle fade-in animation
5. Toast icons: SVG icons for success/danger/warning types
6. Swipe haptic: vibrate(15) fires at threshold during touchmove
This commit is contained in:
Ulas
2026-04-04 07:25:54 +02:00
parent db60279f87
commit 38c5852c78
8 changed files with 147 additions and 21 deletions
+3 -2
View File
@@ -377,9 +377,10 @@ function openBudgetModal({ mode, entry = null }) {
</div>
<div class="form-group">
<label class="allday-toggle">
<label class="toggle">
<input type="checkbox" id="bm-recurring" ${isEdit && entry.is_recurring ? 'checked' : ''}>
<span class="allday-toggle__label">${t('budget.recurringLabel')}</span>
<span class="toggle__track"></span>
<span>${t('budget.recurringLabel')}</span>
</label>
</div>
+3 -2
View File
@@ -795,9 +795,10 @@ function buildEventModalContent({ mode, event, date }) {
</div>
<div class="form-group">
<label class="allday-toggle">
<label class="toggle">
<input type="checkbox" id="modal-allday" ${isEdit && event.all_day ? 'checked' : ''}>
<span class="allday-toggle__label">${t('calendar.allDayToggle')}</span>
<span class="toggle__track"></span>
<span>${t('calendar.allDayToggle')}</span>
</label>
</div>
+3 -2
View File
@@ -379,9 +379,10 @@ function openNoteModal({ mode, note = null }) {
</div>
</div>
<div class="form-group">
<label class="allday-toggle">
<label class="toggle">
<input type="checkbox" id="note-pinned" ${isEdit && note.pinned ? 'checked' : ''}>
<span class="allday-toggle__label">${t('notes.pinnedLabel')}</span>
<span class="toggle__track"></span>
<span>${t('notes.pinnedLabel')}</span>
</label>
</div>
+8
View File
@@ -350,6 +350,7 @@ function wireSwipeGestures(container) {
let startX = 0, startY = 0;
let dx = 0;
let locked = false; // false | 'swipe' | 'scroll'
let thresholdHit = false;
const card = row.querySelector('.shopping-item');
if (!card) return;
@@ -367,6 +368,7 @@ function wireSwipeGestures(container) {
startY = e.touches[0].clientY;
dx = 0;
locked = false;
thresholdHit = false;
card.style.transition = '';
}, { passive: true });
@@ -408,6 +410,12 @@ function wireSwipeGestures(container) {
row.querySelector('.swipe-reveal--delete').style.opacity = String(progress);
row.querySelector('.swipe-reveal--done').style.opacity = '0';
}
// Haptic-Feedback beim Erreichen des Schwellwerts
if (!thresholdHit && Math.abs(dx) >= SWIPE_THRESHOLD) {
thresholdHit = true;
vibrate(15);
}
}, { passive: false });
row.addEventListener('touchend', async () => {
+8
View File
@@ -730,6 +730,7 @@ function wireSwipeGestures(container) {
let startX = 0, startY = 0;
let dx = 0;
let locked = false; // false = unentschieden, 'swipe' | 'scroll'
let thresholdHit = false; // Haptic-Feedback am Threshold nur einmal
const card = row.querySelector('.task-card');
if (!card) return;
@@ -749,6 +750,7 @@ function wireSwipeGestures(container) {
startY = e.touches[0].clientY;
dx = 0;
locked = false;
thresholdHit = false;
card.style.transition = '';
}, { passive: true });
@@ -794,6 +796,12 @@ function wireSwipeGestures(container) {
row.querySelector('.swipe-reveal--edit').style.opacity = String(progress);
row.querySelector('.swipe-reveal--done').style.opacity = '0';
}
// Haptic-Feedback beim Erreichen des Schwellwerts
if (!thresholdHit && Math.abs(dx) >= SWIPE_THRESHOLD) {
thresholdHit = true;
vibrate(15);
}
}, { passive: false });
row.addEventListener('touchend', async () => {