626 lines
13 KiB
CSS
626 lines
13 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
@layer base {
|
|
body {
|
|
@apply bg-gray-50 text-gray-900;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
/* Prevent zoom on input focus on iOS */
|
|
input, select, textarea {
|
|
font-size: 16px !important;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
input, select, textarea {
|
|
font-size: inherit !important;
|
|
}
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
.btn-primary {
|
|
@apply bg-primary-600 text-white px-4 py-2.5 rounded-lg font-medium;
|
|
@apply transition-all duration-200 ease-out;
|
|
@apply hover:bg-primary-700 hover:shadow-md hover:-translate-y-0.5;
|
|
@apply active:scale-95 active:translate-y-0;
|
|
@apply text-sm sm:text-base;
|
|
}
|
|
|
|
.btn-secondary {
|
|
@apply bg-gray-200 text-gray-700 px-4 py-2.5 rounded-lg font-medium;
|
|
@apply transition-all duration-200 ease-out;
|
|
@apply hover:bg-gray-300 hover:shadow-sm hover:-translate-y-0.5;
|
|
@apply active:scale-95 active:translate-y-0;
|
|
@apply text-sm sm:text-base;
|
|
}
|
|
|
|
.btn-danger {
|
|
@apply bg-red-600 text-white px-4 py-2.5 rounded-lg font-medium;
|
|
@apply transition-all duration-200 ease-out;
|
|
@apply hover:bg-red-700 hover:shadow-md hover:-translate-y-0.5;
|
|
@apply active:scale-95 active:translate-y-0;
|
|
@apply text-sm sm:text-base;
|
|
}
|
|
|
|
.card {
|
|
@apply bg-white rounded-lg shadow-sm border border-gray-200 p-4 sm:p-6;
|
|
@apply transition-all duration-300 ease-out;
|
|
}
|
|
|
|
.card-hover {
|
|
@apply hover:shadow-lg hover:-translate-y-1 hover:border-gray-300;
|
|
}
|
|
|
|
.input {
|
|
@apply border border-gray-300 rounded-lg px-3 py-2.5 w-full;
|
|
@apply focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent;
|
|
@apply transition-all duration-200 ease-out;
|
|
@apply text-base sm:text-sm;
|
|
}
|
|
|
|
.label {
|
|
@apply block text-sm font-medium text-gray-700 mb-1;
|
|
}
|
|
|
|
/* Mobile-friendly touch targets */
|
|
.touch-target {
|
|
@apply min-h-[44px] min-w-[44px];
|
|
}
|
|
|
|
/* Page titles */
|
|
.page-title {
|
|
@apply text-2xl sm:text-3xl font-bold text-gray-900;
|
|
}
|
|
|
|
/* Modal styles with animations */
|
|
.modal-overlay {
|
|
@apply fixed inset-0 bg-black/50 flex items-end sm:items-center justify-center z-50;
|
|
animation: fadeIn 0.2s ease-out;
|
|
}
|
|
|
|
.modal-content {
|
|
@apply bg-white w-full sm:max-w-lg sm:mx-4 sm:rounded-lg rounded-t-2xl max-h-[90vh] overflow-hidden;
|
|
animation: slideUp 0.3s ease-out;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.modal-content {
|
|
animation: scaleIn 0.2s ease-out;
|
|
}
|
|
}
|
|
|
|
/* Safe area for bottom navigation */
|
|
.safe-bottom {
|
|
padding-bottom: env(safe-area-inset-bottom, 0);
|
|
}
|
|
|
|
/* Animated list items */
|
|
.list-item-animated {
|
|
@apply transition-all duration-200 ease-out;
|
|
animation: fadeSlideIn 0.3s ease-out backwards;
|
|
}
|
|
|
|
/* Stagger animation delays */
|
|
.stagger-1 { animation-delay: 0.05s; }
|
|
.stagger-2 { animation-delay: 0.1s; }
|
|
.stagger-3 { animation-delay: 0.15s; }
|
|
.stagger-4 { animation-delay: 0.2s; }
|
|
.stagger-5 { animation-delay: 0.25s; }
|
|
.stagger-6 { animation-delay: 0.3s; }
|
|
|
|
/* Pulse animation for loading states */
|
|
.pulse-soft {
|
|
animation: pulseSoft 2s ease-in-out infinite;
|
|
}
|
|
|
|
/* Shimmer loading effect */
|
|
.shimmer {
|
|
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
|
background-size: 200% 100%;
|
|
animation: shimmer 1.5s infinite;
|
|
}
|
|
|
|
/* Icon animation */
|
|
.icon-bounce {
|
|
@apply transition-transform duration-200;
|
|
}
|
|
.icon-bounce:hover {
|
|
animation: iconBounce 0.4s ease-out;
|
|
}
|
|
|
|
/* Floating action button */
|
|
.fab {
|
|
@apply fixed bottom-20 right-4 lg:bottom-8 lg:right-8;
|
|
@apply w-14 h-14 rounded-full shadow-lg;
|
|
@apply flex items-center justify-center;
|
|
@apply bg-primary-600 text-white;
|
|
@apply transition-all duration-300 ease-out;
|
|
@apply hover:shadow-xl hover:scale-110;
|
|
@apply active:scale-95;
|
|
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
animation: fabIn 0.4s ease-out;
|
|
z-index: 40;
|
|
}
|
|
|
|
/* Secondary FAB */
|
|
.fab-secondary {
|
|
@apply fixed bottom-36 right-4 lg:bottom-24 lg:right-8;
|
|
@apply w-12 h-12 rounded-full shadow-md;
|
|
@apply flex items-center justify-center;
|
|
@apply bg-white text-gray-700 border border-gray-200;
|
|
@apply transition-all duration-300 ease-out;
|
|
@apply hover:shadow-lg hover:scale-105;
|
|
@apply active:scale-95;
|
|
animation: fabIn 0.4s ease-out 0.1s backwards;
|
|
z-index: 39;
|
|
}
|
|
|
|
/* Empty state */
|
|
.empty-state {
|
|
@apply text-center py-12 px-4;
|
|
}
|
|
|
|
.empty-state-icon {
|
|
@apply h-16 w-16 sm:h-20 sm:w-20 text-gray-300 mx-auto mb-4;
|
|
animation: fadeSlideIn 0.5s ease-out;
|
|
}
|
|
|
|
.empty-state-text {
|
|
@apply text-gray-500 text-sm sm:text-base mb-4;
|
|
animation: fadeSlideIn 0.5s ease-out 0.1s backwards;
|
|
}
|
|
|
|
/* Touch feedback for list items */
|
|
.list-item-touch {
|
|
@apply relative overflow-hidden;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
.list-item-touch::before {
|
|
content: '';
|
|
@apply absolute inset-0 bg-gray-900/5 opacity-0 transition-opacity duration-150;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.list-item-touch:active::before {
|
|
@apply opacity-100;
|
|
}
|
|
|
|
/* Mobile card improvements */
|
|
@media (max-width: 640px) {
|
|
.card {
|
|
@apply rounded-xl;
|
|
}
|
|
|
|
.btn-primary, .btn-secondary, .btn-danger {
|
|
@apply py-3;
|
|
}
|
|
}
|
|
|
|
/* Pull to refresh indicator */
|
|
.pull-indicator {
|
|
@apply flex items-center justify-center py-4 text-gray-400;
|
|
animation: fadeIn 0.2s ease-out;
|
|
}
|
|
|
|
/* Swipeable item */
|
|
.swipe-item {
|
|
@apply relative;
|
|
touch-action: pan-y;
|
|
}
|
|
|
|
/* Bottom sheet handle */
|
|
.sheet-handle {
|
|
@apply w-10 h-1 bg-gray-300 rounded-full mx-auto mb-2;
|
|
}
|
|
|
|
/* iOS-style segment control */
|
|
.segment-control {
|
|
@apply flex rounded-lg bg-gray-100 p-1 relative;
|
|
}
|
|
|
|
.segment-item {
|
|
@apply flex-1 py-2 px-3 rounded-md text-sm font-medium transition-colors relative z-10;
|
|
@apply flex items-center justify-center;
|
|
}
|
|
|
|
.segment-item-active {
|
|
@apply text-gray-900;
|
|
}
|
|
|
|
.segment-item-inactive {
|
|
@apply text-gray-500;
|
|
}
|
|
|
|
.segment-indicator {
|
|
@apply absolute top-1 bottom-1 bg-white rounded-md shadow-sm transition-all duration-300 ease-out;
|
|
}
|
|
|
|
/* Status indicator */
|
|
.status-online {
|
|
@apply w-2 h-2 bg-green-500 rounded-full;
|
|
animation: pulseSoft 2s ease-in-out infinite;
|
|
}
|
|
|
|
.status-offline {
|
|
@apply w-2 h-2 bg-gray-400 rounded-full;
|
|
}
|
|
|
|
/* Quick action buttons */
|
|
.quick-action {
|
|
@apply flex flex-col items-center justify-center p-3 rounded-xl;
|
|
@apply bg-gray-50 hover:bg-gray-100 active:bg-gray-200;
|
|
@apply transition-all duration-200;
|
|
@apply min-w-[72px];
|
|
}
|
|
|
|
.quick-action-icon {
|
|
@apply w-10 h-10 rounded-full flex items-center justify-center mb-1;
|
|
@apply transition-transform duration-200;
|
|
}
|
|
|
|
.quick-action:active .quick-action-icon {
|
|
@apply scale-90;
|
|
}
|
|
|
|
.quick-action-label {
|
|
@apply text-xs text-gray-600 font-medium;
|
|
}
|
|
|
|
/* Badge animation */
|
|
.badge-pulse {
|
|
@apply relative;
|
|
}
|
|
.badge-pulse::after {
|
|
content: '';
|
|
@apply absolute inset-0 rounded-full bg-current opacity-40;
|
|
animation: badgePulse 1.5s ease-out infinite;
|
|
}
|
|
|
|
/* Skeleton loading */
|
|
.skeleton {
|
|
@apply bg-gray-200 rounded;
|
|
animation: skeleton 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
/* Tooltip animation */
|
|
.tooltip {
|
|
animation: tooltipIn 0.15s ease-out;
|
|
}
|
|
|
|
/* Success checkmark animation */
|
|
.success-check {
|
|
animation: successCheck 0.4s ease-out;
|
|
}
|
|
|
|
/* Shake animation for errors */
|
|
.shake {
|
|
animation: shake 0.5s ease-out;
|
|
}
|
|
|
|
/* Slide transitions */
|
|
.slide-enter {
|
|
animation: slideIn 0.3s ease-out;
|
|
}
|
|
|
|
.slide-exit {
|
|
animation: slideOut 0.2s ease-in forwards;
|
|
}
|
|
|
|
/* Rotate animation for refresh icons */
|
|
.spin-once {
|
|
animation: spinOnce 0.5s ease-out;
|
|
}
|
|
|
|
/* Number counter animation */
|
|
.counter {
|
|
@apply tabular-nums;
|
|
transition: all 0.3s ease-out;
|
|
}
|
|
|
|
/* Progress bar */
|
|
.progress-bar {
|
|
@apply h-1 bg-primary-600 rounded-full;
|
|
animation: progressGrow 0.5s ease-out;
|
|
}
|
|
|
|
/* Ripple effect */
|
|
.ripple {
|
|
@apply relative overflow-hidden;
|
|
}
|
|
.ripple::after {
|
|
content: '';
|
|
@apply absolute inset-0 bg-white/30 rounded-full scale-0;
|
|
transition: transform 0.4s ease-out, opacity 0.4s ease-out;
|
|
}
|
|
.ripple:active::after {
|
|
@apply scale-150 opacity-0;
|
|
}
|
|
}
|
|
|
|
/* Keyframes */
|
|
@keyframes fadeIn {
|
|
from { opacity: 0; }
|
|
to { opacity: 1; }
|
|
}
|
|
|
|
@keyframes fadeOut {
|
|
from { opacity: 1; }
|
|
to { opacity: 0; }
|
|
}
|
|
|
|
@keyframes slideUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(100%);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes slideDown {
|
|
from {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translateY(100%);
|
|
}
|
|
}
|
|
|
|
@keyframes scaleIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0.95);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes fadeSlideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@keyframes slideOut {
|
|
from {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
to {
|
|
opacity: 0;
|
|
transform: translateX(20px);
|
|
}
|
|
}
|
|
|
|
@keyframes pulseSoft {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.6; }
|
|
}
|
|
|
|
@keyframes shimmer {
|
|
0% { background-position: -200% 0; }
|
|
100% { background-position: 200% 0; }
|
|
}
|
|
|
|
@keyframes iconBounce {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-3px); }
|
|
}
|
|
|
|
@keyframes fabIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: scale(0) rotate(-180deg);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: scale(1) rotate(0);
|
|
}
|
|
}
|
|
|
|
@keyframes badgePulse {
|
|
0% {
|
|
transform: scale(1);
|
|
opacity: 0.4;
|
|
}
|
|
100% {
|
|
transform: scale(2);
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
@keyframes skeleton {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.5; }
|
|
}
|
|
|
|
@keyframes tooltipIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(5px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
@keyframes successCheck {
|
|
0% {
|
|
transform: scale(0);
|
|
}
|
|
50% {
|
|
transform: scale(1.2);
|
|
}
|
|
100% {
|
|
transform: scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes shake {
|
|
0%, 100% { transform: translateX(0); }
|
|
20% { transform: translateX(-8px); }
|
|
40% { transform: translateX(8px); }
|
|
60% { transform: translateX(-4px); }
|
|
80% { transform: translateX(4px); }
|
|
}
|
|
|
|
@keyframes spinOnce {
|
|
from { transform: rotate(0deg); }
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
@keyframes progressGrow {
|
|
from { width: 0; }
|
|
}
|
|
|
|
@keyframes bounce {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-5px); }
|
|
}
|
|
|
|
@keyframes wiggle {
|
|
0%, 100% { transform: rotate(0deg); }
|
|
25% { transform: rotate(-5deg); }
|
|
75% { transform: rotate(5deg); }
|
|
}
|
|
|
|
/* Leaflet map container */
|
|
.leaflet-container {
|
|
height: 100%;
|
|
width: 100%;
|
|
border-radius: 0.5rem;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* Fix leaflet controls on mobile */
|
|
.leaflet-control-zoom {
|
|
border: none !important;
|
|
box-shadow: 0 2px 6px rgba(0,0,0,0.15) !important;
|
|
}
|
|
|
|
.leaflet-control-zoom a {
|
|
width: 36px !important;
|
|
height: 36px !important;
|
|
line-height: 36px !important;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
/* Custom scrollbar for mobile */
|
|
@media (max-width: 640px) {
|
|
::-webkit-scrollbar {
|
|
width: 4px;
|
|
height: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #d1d5db;
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
|
|
/* Hide scrollbar but keep functionality */
|
|
.hide-scrollbar {
|
|
-ms-overflow-style: none;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.hide-scrollbar::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
/* Smooth scroll on iOS */
|
|
.smooth-scroll {
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
/* Pull to refresh indicator space */
|
|
@supports (padding-top: env(safe-area-inset-top)) {
|
|
.safe-top {
|
|
padding-top: env(safe-area-inset-top);
|
|
}
|
|
}
|
|
|
|
/* Page transition wrapper */
|
|
.page-transition {
|
|
animation: pageIn 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes pageIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Blob animation for login page */
|
|
@keyframes blob {
|
|
0%, 100% {
|
|
transform: translate(0, 0) scale(1);
|
|
}
|
|
33% {
|
|
transform: translate(30px, -50px) scale(1.1);
|
|
}
|
|
66% {
|
|
transform: translate(-20px, 20px) scale(0.9);
|
|
}
|
|
}
|
|
|
|
.animate-blob {
|
|
animation: blob 7s infinite;
|
|
}
|
|
|
|
.animation-delay-2000 {
|
|
animation-delay: 2s;
|
|
}
|
|
|
|
.animation-delay-4000 {
|
|
animation-delay: 4s;
|
|
}
|
|
|
|
/* Shake animation for errors */
|
|
@keyframes shake {
|
|
0%, 100% { transform: translateX(0); }
|
|
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
|
|
20%, 40%, 60%, 80% { transform: translateX(10px); }
|
|
}
|
|
|
|
/* Reduced motion */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
}
|
|
}
|