feat: add fancy animation to Mark as Read button

- Icon spins 360° with bounce effect (scale up during spin)
- Button background changes to vibrant green gradient (#10b981)
- Green pulsing box-shadow effect on activation
- Button scales up slightly on click for emphasis
- Holds green state for 1.5 seconds
- Smoothly fades to gray after animation
- Final state is gray button to indicate marked status
- Uses cubic-bezier easing for modern, smooth feel
- Total animation duration: 2.5 seconds
- Prevents interaction during animation
This commit is contained in:
Gigi
2025-10-15 23:39:14 +02:00
parent 2fc64b6028
commit 1982d25fa8
2 changed files with 68 additions and 3 deletions

View File

@@ -203,10 +203,10 @@ const ContentPanel: React.FC<ContentPanelProps> = ({
setIsMarkedAsRead(true) setIsMarkedAsRead(true)
setShowCheckAnimation(true) setShowCheckAnimation(true)
// Reset animation after it completes // Reset animation after it completes (2.5s for full fancy animation)
setTimeout(() => { setTimeout(() => {
setShowCheckAnimation(false) setShowCheckAnimation(false)
}, 600) }, 2500)
// Fire-and-forget: publish in background without blocking UI // Fire-and-forget: publish in background without blocking UI
;(async () => { ;(async () => {

View File

@@ -216,7 +216,72 @@
.mark-as-read-btn:hover:not(:disabled) { background: var(--color-border); border-color: var(--color-text-muted); transform: translateY(-1px); } .mark-as-read-btn:hover:not(:disabled) { background: var(--color-border); border-color: var(--color-text-muted); transform: translateY(-1px); }
.mark-as-read-btn:active:not(:disabled) { transform: translateY(0); } .mark-as-read-btn:active:not(:disabled) { transform: translateY(0); }
.mark-as-read-btn:disabled { opacity: 0.6; cursor: not-allowed; } .mark-as-read-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.mark-as-read-btn svg { font-size: 1.1rem; } .mark-as-read-btn svg { font-size: 1.1rem; transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); }
/* Fancy Mark as Read animation */
@keyframes markAsReadSuccess {
0% {
background: var(--color-bg-elevated);
border-color: var(--color-border-subtle);
transform: scale(1);
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
}
10% {
transform: scale(1.05);
box-shadow: 0 0 0 8px rgba(16, 185, 129, 0.3);
}
25% {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
border-color: #10b981;
color: white;
transform: scale(1.02);
box-shadow: 0 4px 20px rgba(16, 185, 129, 0.4);
}
65% {
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
border-color: #10b981;
color: white;
transform: scale(1.02);
box-shadow: 0 4px 20px rgba(16, 185, 129, 0.4);
}
100% {
background: #6b7280;
border-color: #6b7280;
color: white;
transform: scale(1);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
}
@keyframes iconSpin {
0% {
transform: rotate(0deg) scale(1);
}
15% {
transform: rotate(0deg) scale(1.2);
}
50% {
transform: rotate(360deg) scale(1.2);
}
100% {
transform: rotate(360deg) scale(1);
}
}
.mark-as-read-btn.animating {
animation: markAsReadSuccess 2.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
pointer-events: none;
}
.mark-as-read-btn.animating svg {
animation: iconSpin 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}
.mark-as-read-btn.marked {
background: #6b7280;
border-color: #6b7280;
color: white;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.reader { .reader {
max-width: 100%; max-width: 100%;