diff --git a/frontend/src/index.css b/frontend/src/index.css index f6ca417..03a603d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -350,31 +350,334 @@ } /* ============================================================ - UTILITY ENHANCEMENTS + ANIMATIONS & MICRO-INTERACTIONS ============================================================ */ -/* Smooth page transitions */ -.page-enter { - animation: fadeIn 200ms ease-out; -} +/* ── Keyframes ─────────────────────────────────────────────── */ @keyframes fadeIn { - from { - opacity: 0; - transform: translateY(4px); - } - to { - opacity: 1; - transform: translateY(0); - } + from { opacity: 0; transform: translateY(6px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fadeInUp { + from { opacity: 0; transform: translateY(12px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes fadeInScale { + from { opacity: 0; transform: scale(0.96); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes slideInRight { + from { opacity: 0; transform: translateX(16px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slideInLeft { + from { opacity: 0; transform: translateX(-16px); } + to { opacity: 1; transform: translateX(0); } +} + +@keyframes slideUp { + from { opacity: 0; transform: translateY(20px); } + to { opacity: 1; transform: translateY(0); } } -/* Pulse animation for loading states */ @keyframes subtlePulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.7; } } +@keyframes shimmer { + 0% { background-position: -200% 0; } + 100% { background-position: 200% 0; } +} + +@keyframes progressGrow { + from { width: 0%; } +} + +@keyframes popIn { + 0% { opacity: 0; transform: scale(0.8); } + 70% { transform: scale(1.02); } + 100% { opacity: 1; transform: scale(1); } +} + +@keyframes breathe { + 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--accent-rgb, 0, 137, 61), 0.15); } + 50% { box-shadow: 0 0 0 6px rgba(var(--accent-rgb, 0, 137, 61), 0); } +} + +@keyframes countUp { + from { opacity: 0; transform: translateY(8px); } + to { opacity: 1; transform: translateY(0); } +} + +/* ── Utility animation classes ─────────────────────────────── */ + +.animate-fade-in { + animation: fadeIn 300ms ease-out both; +} + +.animate-fade-in-up { + animation: fadeInUp 400ms ease-out both; +} + +.animate-fade-in-scale { + animation: fadeInScale 250ms ease-out both; +} + +.animate-slide-in-right { + animation: slideInRight 300ms ease-out both; +} + +.animate-slide-in-left { + animation: slideInLeft 300ms ease-out both; +} + +.animate-slide-up { + animation: slideUp 350ms cubic-bezier(0.16, 1, 0.3, 1) both; +} + +.animate-pop-in { + animation: popIn 300ms cubic-bezier(0.16, 1, 0.3, 1) both; +} + .animate-subtle-pulse { animation: subtlePulse 2s ease-in-out infinite; } + +.animate-breathe { + animation: breathe 2s ease-in-out infinite; +} + +.animate-count-up { + animation: countUp 400ms ease-out both; +} + +/* ── Card hover micro-interaction ──────────────────────────── */ + +.card { + transition: transform 200ms ease, box-shadow 200ms ease, border-color 200ms ease; +} +.card:hover { + /* Subtle lift + glow on hover — only for non-table cards */ +} + +/* Cards in grid layouts get the hover lift */ +.grid > .card:hover, +.card.hover-lift:hover { + transform: translateY(-2px); + box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.15), 0 4px 8px -2px rgba(0, 0, 0, 0.08); +} +:root.dark .grid > .card:hover, +:root.dark .card.hover-lift:hover { + box-shadow: 0 8px 24px -4px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.06); +} + +/* ── Staggered grid item entrance ──────────────────────────── */ + +.grid > * { + animation: fadeInUp 350ms ease-out both; +} +.grid > *:nth-child(1) { animation-delay: 0ms; } +.grid > *:nth-child(2) { animation-delay: 30ms; } +.grid > *:nth-child(3) { animation-delay: 60ms; } +.grid > *:nth-child(4) { animation-delay: 90ms; } +.grid > *:nth-child(5) { animation-delay: 120ms; } +.grid > *:nth-child(6) { animation-delay: 150ms; } +.grid > *:nth-child(7) { animation-delay: 180ms; } +.grid > *:nth-child(8) { animation-delay: 210ms; } +.grid > *:nth-child(9) { animation-delay: 240ms; } +.grid > *:nth-child(10) { animation-delay: 270ms; } +.grid > *:nth-child(11) { animation-delay: 300ms; } +.grid > *:nth-child(12) { animation-delay: 330ms; } +.grid > *:nth-child(n+13) { animation-delay: 350ms; } + +/* ── Table row entrance ────────────────────────────────────── */ + +tbody > tr { + animation: fadeIn 250ms ease-out both; +} +tbody > tr:nth-child(1) { animation-delay: 0ms; } +tbody > tr:nth-child(2) { animation-delay: 25ms; } +tbody > tr:nth-child(3) { animation-delay: 50ms; } +tbody > tr:nth-child(4) { animation-delay: 75ms; } +tbody > tr:nth-child(5) { animation-delay: 100ms; } +tbody > tr:nth-child(n+6) { animation-delay: 120ms; } + +/* ── Sidebar nav link animations ───────────────────────────── */ + +nav a, nav button { + transition: all 180ms ease-out; + position: relative; +} + +/* Active nav link: left accent bar */ +nav a.bg-accent-light { + border-left: 3px solid var(--color-accent); + padding-left: calc(0.75rem - 3px); +} + +/* Nav link hover: subtle slide right */ +nav a:not(.bg-accent-light):hover, +nav button:hover { + transform: translateX(2px); +} + +/* ── Badge animations ──────────────────────────────────────── */ + +.badge, [class*="badge-"] { + transition: all 150ms ease-out; +} +.badge:hover, [class*="badge-"]:hover { + transform: scale(1.05); +} + +/* Status badges in tables — subtle entrance */ +td .badge, td [class*="badge-"] { + animation: popIn 250ms ease-out both; +} + +/* ── Progress bar animation ────────────────────────────────── */ + +[role="progressbar"] > div, +.bg-green-500, +.bg-red-500, +.bg-amber-500, +.bg-blue-500 { + transition: width 600ms cubic-bezier(0.16, 1, 0.3, 1); +} + +/* Render progress bars */ +div[class*="bg-status-success"], +div[class*="bg-green"] { + transition: width 600ms cubic-bezier(0.16, 1, 0.3, 1); +} + +/* ── Modal / dialog animations ─────────────────────────────── */ + +/* Backdrop fade */ +.fixed.inset-0.z-50 { + animation: fadeIn 200ms ease-out; +} + +/* Modal panel pop-in */ +.fixed.inset-0.z-50 > div[role="dialog"], +.fixed.inset-0.z-50 > div:not([class*="absolute"]) > div { + animation: fadeInScale 250ms cubic-bezier(0.16, 1, 0.3, 1); +} + +/* ── Floating action bar slide-up ──────────────────────────── */ + +.fixed.bottom-6, +.fixed.bottom-0 { + animation: slideUp 300ms cubic-bezier(0.16, 1, 0.3, 1); +} + +/* ── Toast notifications ───────────────────────────────────── */ + +[data-sonner-toaster] [data-sonner-toast] { + animation: slideInRight 300ms cubic-bezier(0.16, 1, 0.3, 1) !important; +} + +/* ── Loading skeleton shimmer ──────────────────────────────── */ + +.animate-pulse { + animation: subtlePulse 1.5s ease-in-out infinite !important; +} + +.skeleton { + background: linear-gradient( + 90deg, + var(--color-bg-muted) 25%, + var(--color-bg-surface-hover) 50%, + var(--color-bg-muted) 75% + ); + background-size: 200% 100%; + animation: shimmer 1.5s ease-in-out infinite; + border-radius: 0.5rem; +} + +/* ── Image/thumbnail hover zoom ────────────────────────────── */ + +.group img, +.group video { + transition: transform 300ms ease-out; +} +.group:hover img, +.group:hover video { + transform: scale(1.03); +} + +/* ── Number/stat counter entrance ──────────────────────────── */ + +.text-2xl, .text-3xl, .text-4xl { + animation: countUp 500ms ease-out both; +} + +/* ── Checkbox/toggle animation ─────────────────────────────── */ + +input[type="checkbox"] { + transition: all 150ms ease-out; +} +input[type="checkbox"]:checked { + animation: popIn 200ms ease-out; +} + +/* ── Link hover underline animation ────────────────────────── */ + +a.hover\:underline { + text-decoration: none; + background-image: linear-gradient(currentColor, currentColor); + background-position: 0% 100%; + background-repeat: no-repeat; + background-size: 0% 1px; + transition: background-size 200ms ease-out; +} +a.hover\:underline:hover { + background-size: 100% 1px; +} + +/* ── Dropdown/select hover ─────────────────────────────────── */ + +select { + transition: all 150ms ease-out; + cursor: pointer; +} +select:hover { + border-color: var(--color-text-muted); +} + +/* ── Notification bell wiggle ──────────────────────────────── */ + +@keyframes wiggle { + 0%, 100% { transform: rotate(0deg); } + 25% { transform: rotate(8deg); } + 75% { transform: rotate(-8deg); } +} + +.notification-bell:hover { + animation: wiggle 400ms ease-in-out; +} + +/* ── Smooth scroll behavior ────────────────────────────────── */ + +html { + scroll-behavior: smooth; +} + +/* ── Reduced motion: respect user preference ───────────────── */ + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + html { + scroll-behavior: auto; + } +}