Coffee journaling on ATProto (alpha) alpha.arabica.social
coffee
at main 756 lines 18 kB view raw
1@font-face { 2 font-family: 'Iosevka Patrick'; 3 src: url('/static/fonts/IosevkaPatrickNerdFont-Regular.woff2') format('woff2'); 4 font-weight: 400; 5 font-style: normal; 6 font-display: swap; 7} 8 9@font-face { 10 font-family: 'Iosevka Patrick'; 11 src: url('/static/fonts/IosevkaPatrickNerdFont-Medium.woff2') format('woff2'); 12 font-weight: 500; 13 font-style: normal; 14 font-display: swap; 15} 16 17@font-face { 18 font-family: 'Iosevka Patrick'; 19 src: url('/static/fonts/IosevkaPatrickNerdFont-SemiBold.woff2') format('woff2'); 20 font-weight: 600; 21 font-style: normal; 22 font-display: swap; 23} 24 25@tailwind base; 26@tailwind components; 27@tailwind utilities; 28 29@layer base { 30 h1, h2, h3 { 31 @apply font-semibold; 32 } 33 34 /* Touch targets for interactive elements only */ 35 button, 36 input[type="submit"], 37 input[type="button"] { 38 min-height: 44px; 39 min-width: 44px; 40 } 41 42 /* Prevent iOS zoom on input focus */ 43 @media (max-width: 768px) { 44 input, 45 select, 46 textarea { 47 font-size: 16px; 48 } 49 } 50} 51 52@layer components { 53 /* Page Containers */ 54 .page-container { 55 @apply mx-auto px-2 sm:px-4; 56 } 57 58 .page-container-sm { 59 @apply page-container max-w-2xl; 60 } 61 62 .page-container-md { 63 @apply page-container max-w-3xl; 64 } 65 66 .page-container-lg { 67 @apply page-container max-w-4xl; 68 } 69 70 .page-container-xl { 71 @apply page-container max-w-6xl; 72 } 73 74 /* Cards and Containers */ 75 .card { 76 @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-xl border border-brown-300; 77 } 78 79 .card-inner { 80 @apply p-6; 81 } 82 83 .card-sm { 84 @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-lg shadow-md border border-brown-300; 85 } 86 87 /* Section box for lighter content areas */ 88 .section-box { 89 @apply bg-brown-50 rounded-lg p-4 border border-brown-200; 90 } 91 92 /* Buttons */ 93 .btn { 94 @apply inline-flex items-center justify-center px-4 py-2 rounded-lg font-medium transition-colors cursor-pointer; 95 } 96 97 .btn-primary { 98 @apply btn bg-gradient-to-br from-brown-700 to-brown-900 text-white hover:from-brown-800 hover:to-brown-900 shadow-md; 99 } 100 101 .btn-secondary { 102 @apply btn bg-brown-300 text-brown-900 hover:bg-brown-400; 103 } 104 105 .btn-tertiary { 106 @apply btn bg-gradient-to-br from-brown-500 to-brown-600 text-white hover:from-brown-600 hover:to-brown-700; 107 } 108 109 .btn-link { 110 @apply text-brown-700 hover:text-brown-900 font-medium underline transition-colors cursor-pointer; 111 } 112 113 .btn-danger { 114 @apply text-red-600 hover:text-red-800 font-medium underline transition-colors cursor-pointer; 115 } 116 117 /* Forms */ 118 .form-label { 119 @apply block text-sm font-medium text-brown-900 mb-2; 120 } 121 122 .form-input { 123 @apply rounded-lg border-2 border-brown-300 shadow-sm focus:border-brown-600 focus:ring-brown-600 text-base py-2 px-3 bg-white; 124 } 125 126 .form-input-lg { 127 @apply form-input py-3 px-4; 128 } 129 130 .form-select { 131 @apply form-input truncate max-w-full; 132 } 133 134 .form-textarea { 135 @apply form-input min-h-[100px]; 136 } 137 138 /* Tables */ 139 .table-container { 140 @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-lg shadow-md overflow-hidden border border-brown-300; 141 } 142 143 .table { 144 @apply min-w-full divide-y divide-brown-300; 145 } 146 147 .table-header { 148 @apply bg-brown-200; 149 } 150 151 .table-th { 152 @apply px-6 py-3 text-left text-xs font-medium text-brown-900 uppercase tracking-wider; 153 } 154 155 .table-body { 156 @apply bg-brown-100 divide-y divide-brown-200; 157 } 158 159 .table-row { 160 @apply hover:bg-brown-100 transition-colors; 161 } 162 163 .table-td { 164 @apply px-6 py-4 whitespace-nowrap text-sm text-brown-800; 165 } 166 167 /* Modals */ 168 .modal-backdrop { 169 @apply fixed inset-0 bg-black/40 backdrop-blur-sm flex items-center justify-center z-50 p-4; 170 } 171 172 .modal-content { 173 @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-2xl p-6 max-w-md w-full border border-brown-300 max-h-[90vh] overflow-y-auto; 174 } 175 176 .modal-title { 177 @apply text-xl font-semibold text-brown-900 mb-4; 178 } 179 180 /* Native Dialog Element */ 181 .modal-dialog { 182 @apply p-0 bg-transparent border-none shadow-none max-w-md w-full; 183 } 184 185 .modal-dialog::backdrop { 186 @apply bg-black/40 backdrop-blur-sm; 187 } 188 189 /* Dialog content wrapper (nested inside dialog) */ 190 .modal-dialog .modal-content { 191 @apply bg-gradient-to-br from-brown-100 to-brown-200 rounded-xl shadow-2xl p-6 w-full border border-brown-300 max-h-[90vh] overflow-y-auto; 192 } 193 194 /* Entity suggestion typeahead dropdown */ 195 .suggestions-dropdown { 196 @apply absolute z-50 left-0 right-0 mt-1 bg-white rounded-lg shadow-lg border border-brown-200 max-h-48 overflow-y-auto; 197 } 198 199 .suggestions-item { 200 @apply w-full text-left px-3 py-2 flex items-center gap-2 hover:bg-brown-50 transition-colors cursor-pointer border-b border-brown-100 last:border-b-0; 201 } 202 203 /* Typography */ 204 .section-title { 205 @apply text-2xl font-bold text-brown-900 mb-4; 206 } 207 208 .page-title { 209 @apply text-3xl font-bold text-brown-900; 210 } 211 212 /* Feed Components */ 213 .feed-card { 214 @apply bg-gradient-to-br from-brown-50 to-brown-100 rounded-lg shadow-md border border-brown-200 p-3 sm:p-4 hover:shadow-lg transition-shadow; 215 } 216 217 .feed-content-box { 218 @apply bg-white/60 backdrop-blur rounded-lg p-3 sm:p-4 border border-brown-200; 219 } 220 221 .feed-content-box-sm { 222 @apply bg-white/60 backdrop-blur rounded-lg p-2 sm:p-3 border border-brown-200; 223 } 224 225 /* Avatar - base styles */ 226 .avatar { 227 @apply rounded-full object-cover transition-all duration-200; 228 } 229 230 .avatar-sm { 231 @apply avatar w-8 h-8 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; 232 } 233 234 .avatar-md { 235 @apply avatar w-12 h-12 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; 236 } 237 238 .avatar-lg { 239 @apply avatar w-20 h-20 border-2 border-brown-300 hover:border-brown-400 hover:border-4; 240 } 241 242 /* Avatar placeholder - for missing images */ 243 .avatar-placeholder { 244 @apply rounded-full bg-brown-300 flex items-center justify-center transition-all duration-200; 245 } 246 247 .avatar-placeholder-sm { 248 @apply avatar-placeholder w-8 h-8 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; 249 } 250 251 .avatar-placeholder-md { 252 @apply avatar-placeholder w-12 h-12 ring-2 ring-brown-500 hover:ring-brown-400 hover:ring-4; 253 } 254 255 .avatar-placeholder-lg { 256 @apply avatar-placeholder w-20 h-20 hover:bg-brown-400; 257 } 258 259 /* Avatar text sizing */ 260 .avatar-text-sm { 261 @apply text-brown-600 text-sm; 262 } 263 264 .avatar-text-md { 265 @apply text-brown-600 text-base; 266 } 267 268 .avatar-text-lg { 269 @apply text-brown-600 text-2xl; 270 } 271 272 /* Text Utilities */ 273 .text-helper { 274 @apply text-sm text-brown-700 mt-1; 275 } 276 277 .text-meta { 278 @apply text-xs text-brown-600; 279 } 280 281 .text-meta-sm { 282 @apply text-sm text-brown-600; 283 } 284 285 .text-label { 286 @apply text-brown-600; 287 } 288 289 /* Badges */ 290 .badge-rating { 291 @apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-amber-100 text-amber-900 flex-shrink-0; 292 } 293 294 .badge-rating-sm { 295 @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-amber-100 text-amber-900; 296 } 297 298 /* Links */ 299 .link { 300 @apply text-brown-700 hover:text-brown-900 hover:underline transition-colors; 301 } 302 303 .link-bold { 304 @apply font-medium text-brown-700 hover:text-brown-900 hover:underline transition-colors; 305 } 306 307 /* Like Button */ 308 .like-btn { 309 @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors min-h-[44px]; 310 } 311 312 .like-btn-liked { 313 @apply like-btn bg-brown-100 text-red-600 hover:bg-brown-200; 314 animation: like-pop 400ms ease-out; 315 } 316 317 .like-btn-unliked { 318 @apply like-btn bg-brown-100 text-brown-600 hover:bg-brown-200; 319 animation: like-shrink 200ms ease-out; 320 } 321 322 /* Share Button */ 323 .share-btn { 324 @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 min-h-[44px]; 325 } 326 327 /* Comment Button */ 328 .comment-btn { 329 @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 min-h-[44px]; 330 } 331 332 /* Comment Section */ 333 .comment-section { 334 @apply mt-8 pt-6 border-t-2 border-brown-200; 335 } 336 337 .comment-section-header { 338 @apply mb-5; 339 } 340 341 .comment-count-badge { 342 @apply inline-flex items-center justify-center text-xs font-bold bg-brown-700 text-brown-100 rounded-full min-w-[1.375rem] h-[1.375rem] px-1.5; 343 } 344 345 .comment-login-prompt { 346 @apply flex items-center gap-3 bg-brown-50 rounded-lg p-4 mb-5 border border-dashed border-brown-300; 347 } 348 349 .comment-compose { 350 @apply bg-brown-50 rounded-lg p-4 mb-5 border border-brown-200 flex flex-col gap-2; 351 } 352 353 .comment-textarea { 354 @apply w-full rounded-lg border-2 border-brown-200 bg-white px-3 py-2.5 text-base text-brown-900 placeholder-brown-400 resize-none transition-colors focus:border-brown-500 focus:ring-0 focus:outline-none; 355 } 356 357 .comment-list { 358 @apply space-y-1; 359 } 360 361 .comment-empty-state { 362 @apply text-center py-8; 363 } 364 365 .comment-item { 366 @apply relative rounded-lg p-3 transition-colors hover:bg-brown-50/60; 367 } 368 369 .comment-item-inner { 370 @apply relative; 371 } 372 373 .comment-depth-1 { 374 @apply ml-6 pl-4; 375 } 376 377 .comment-depth-2 { 378 @apply ml-12 pl-4; 379 } 380 381 .comment-thread-line { 382 @apply absolute left-0 top-3 bottom-3 w-0.5 bg-brown-200 rounded-full; 383 } 384 385 .comment-reply-btn { 386 @apply inline-flex items-center gap-1 text-brown-400 hover:text-brown-700 transition-colors text-xs font-medium; 387 } 388 389 .comment-delete-btn { 390 @apply text-brown-300 hover:text-brown-600 transition-colors; 391 } 392 393 .comment-reply-form { 394 @apply flex flex-col gap-2 bg-brown-50 rounded-lg p-3 border border-brown-200; 395 } 396 397 /* Action Bar */ 398 .action-bar { 399 @apply flex items-center gap-2 mt-3 pt-3 border-t border-brown-200; 400 } 401 402 /* Action bar inside brew view container - no separator needed */ 403 .brew-view-actions .action-bar { 404 @apply mt-0 pt-0 border-t-0; 405 } 406 407 /* Compact action bar variant for comments */ 408 .comment-item .action-bar { 409 @apply mt-1 border-t-0 gap-1 bg-brown-100 rounded-lg px-1.5 py-1 inline-flex items-center; 410 } 411 412 .comment-item .action-btn, 413 .comment-item .like-btn { 414 @apply px-2 py-1 text-xs min-h-[28px] bg-transparent; 415 } 416 417 .action-btn { 418 @apply inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors bg-brown-100 text-brown-600 hover:bg-brown-200 cursor-pointer min-h-[44px]; 419 } 420 421 .action-btn-liked { 422 @apply text-red-600; 423 animation: like-pop 400ms ease-out; 424 } 425 426 .action-btn-disabled { 427 @apply opacity-50 cursor-not-allowed hover:bg-brown-100; 428 } 429 430 /* Action Menu (More dropdown) */ 431 .action-menu { 432 @apply absolute left-1/2 -translate-x-1/2 w-36 bg-white rounded-lg shadow-lg border border-brown-200 py-1 z-50; 433 } 434 435 /* Dropdown menu (top-positioned, for headers/nav) */ 436 .dropdown-menu { 437 @apply absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-brown-200 py-1 z-50; 438 } 439 440 .dropdown-item { 441 @apply block px-4 py-2 text-sm text-brown-700 hover:bg-brown-50 transition-colors; 442 } 443 444 .dropdown-item-disabled { 445 @apply block px-4 py-2 text-sm text-brown-400 cursor-not-allowed; 446 } 447 448 .dropdown-item-mod { 449 @apply text-amber-700 hover:bg-amber-50; 450 } 451 452 .dropdown-header { 453 @apply px-4 py-2 border-b border-brown-100; 454 } 455 456 .dropdown-divider { 457 @apply border-t border-brown-100 mt-1 pt-1; 458 } 459 460 .action-menu-item { 461 @apply flex items-center gap-2 w-full px-3 py-2 text-sm text-brown-700 hover:bg-brown-100 transition-colors cursor-pointer text-left; 462 } 463 464 .action-menu-item-danger { 465 @apply text-red-600 hover:bg-red-50; 466 } 467 468 .action-menu-item-warning { 469 @apply text-amber-600 hover:bg-amber-50; 470 } 471 472 .action-menu-divider { 473 @apply border-t border-brown-200 my-1; 474 } 475 476 /* Hidden record indicator badge */ 477 .hidden-badge { 478 @apply inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-amber-100 text-amber-700 rounded; 479 } 480} 481 482/* ======================================== 483 HTMX Transition Classes 484 ======================================== */ 485 486/* CRITICAL: Prevent content from being stuck invisible during history restore */ 487/* Override HTMX transition classes during history restoration */ 488body.htmx-history-restoring main, 489body.htmx-history-restoring .htmx-swapping, 490body.htmx-history-restoring .htmx-transitioning { 491 opacity: 1 !important; 492 transform: none !important; 493 animation: none !important; 494 transition: none !important; 495} 496 497/* During swap transition */ 498.htmx-swapping { 499 opacity: 0; 500 transform: translateY(5px); 501 transition: 502 opacity 100ms ease-out, 503 transform 100ms ease-out; 504} 505 506/* After content is added */ 507.htmx-added { 508 opacity: 0; 509 animation: fade-in-slide-up 200ms ease-out forwards; 510} 511 512/* During settling phase */ 513.htmx-settling { 514 opacity: 1; 515 transform: translateY(0); 516 transition: 517 opacity 100ms ease-in, 518 transform 100ms ease-in; 519} 520 521/* Transitioning state (no overlay - looks bad) */ 522.htmx-transitioning { 523 position: relative; 524 /* Removed overlay ::after pseudo-element - it created unwanted shadow effects */ 525} 526 527/* ======================================== 528 Component-Specific Transitions 529 ======================================== */ 530 531/* Feed cards appear with stagger effect */ 532.feed-card { 533 animation: fade-in-slide-up 300ms ease-out backwards; 534} 535 536.feed-card:nth-child(1) { 537 animation-delay: 0ms; 538} 539.feed-card:nth-child(2) { 540 animation-delay: 50ms; 541} 542.feed-card:nth-child(3) { 543 animation-delay: 100ms; 544} 545.feed-card:nth-child(4) { 546 animation-delay: 150ms; 547} 548.feed-card:nth-child(5) { 549 animation-delay: 200ms; 550} 551.feed-card:nth-child(n + 6) { 552 animation-delay: 250ms; 553} 554 555/* Table rows slide in with stagger effect (dynamic content) */ 556.table-body tr { 557 animation: fade-in-slide-up 300ms ease-out backwards; 558} 559 560.table-body tr:nth-child(1) { 561 animation-delay: 0ms; 562} 563.table-body tr:nth-child(2) { 564 animation-delay: 30ms; 565} 566.table-body tr:nth-child(3) { 567 animation-delay: 60ms; 568} 569.table-body tr:nth-child(4) { 570 animation-delay: 90ms; 571} 572.table-body tr:nth-child(5) { 573 animation-delay: 120ms; 574} 575.table-body tr:nth-child(n + 6) { 576 animation-delay: 150ms; 577} 578 579/* Modal transitions (enhanced) */ 580.modal-backdrop { 581 animation: fade-in 100ms ease-out; 582} 583 584.modal-content { 585 animation: modal-appear 150ms ease-out; 586} 587 588@keyframes modal-appear { 589 from { 590 opacity: 0; 591 transform: scale(0.9) translateY(-20px); 592 } 593 to { 594 opacity: 1; 595 transform: scale(1) translateY(0); 596 } 597} 598 599/* HTML5 Dialog Element Transitions */ 600.modal-dialog { 601 opacity: 0; 602 transform: scale(0.95); 603 transition: 604 opacity 100ms ease-out, 605 transform 100ms ease-out, 606 overlay 100ms ease-out allow-discrete, 607 display 100ms ease-out allow-discrete; 608} 609 610.modal-dialog[open] { 611 opacity: 1; 612 transform: scale(1); 613} 614 615/* Backdrop fade-in transition */ 616.modal-dialog::backdrop { 617 background-color: rgba(0, 0, 0, 0); 618 transition: 619 background-color 100ms ease-out, 620 backdrop-filter 100ms ease-out, 621 display 100ms ease-out allow-discrete, 622 overlay 100ms ease-out allow-discrete; 623} 624 625.modal-dialog[open]::backdrop { 626 background-color: rgba(0, 0, 0, 0.4); 627} 628 629/* Starting style for new browsers that support @starting-style */ 630@supports (transition-behavior: allow-discrete) { 631 @starting-style { 632 .modal-dialog[open] { 633 opacity: 0; 634 transform: scale(0.95); 635 } 636 637 .modal-dialog[open]::backdrop { 638 background-color: rgba(0, 0, 0, 0); 639 } 640 } 641} 642 643/* Modal content inside dialog - no separate animation needed */ 644/* The dialog element itself handles the transition */ 645 646/* Form elements slide in */ 647.form-input, 648.form-select, 649.form-textarea { 650 transition: 651 border-color 100ms ease, 652 box-shadow 100ms ease, 653 transform 50ms ease; 654} 655 656.form-input:focus, 657.form-select:focus, 658.form-textarea:focus { 659 transform: translateY(-1px); 660} 661 662/* ======================================== 663 Keyframes 664 ======================================== */ 665 666@keyframes fade-in { 667 from { 668 opacity: 0; 669 } 670 to { 671 opacity: 1; 672 } 673} 674 675@keyframes fade-in-slide-up { 676 from { 677 opacity: 0; 678 transform: translateY(10px); 679 } 680 to { 681 opacity: 1; 682 transform: translateY(0); 683 } 684} 685 686/* Like button pop animation */ 687@keyframes like-pop { 688 0% { 689 transform: scale(1); 690 } 691 15% { 692 transform: scale(1.3); 693 } 694 30% { 695 transform: scale(0.9); 696 } 697 45% { 698 transform: scale(1.15); 699 } 700 60% { 701 transform: scale(0.95); 702 } 703 75% { 704 transform: scale(1.05); 705 } 706 100% { 707 transform: scale(1); 708 } 709} 710 711/* Like button shrink animation for unlike */ 712@keyframes like-shrink { 713 0% { 714 transform: scale(1); 715 } 716 50% { 717 transform: scale(0.8); 718 } 719 100% { 720 transform: scale(1); 721 } 722} 723 724/* ======================================== 725 Loading States 726 ======================================== */ 727 728/* Enhance skeleton loading animations */ 729.animate-pulse { 730 animation: enhanced-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite; 731} 732 733@keyframes enhanced-pulse { 734 0%, 735 100% { 736 opacity: 1; 737 } 738 50% { 739 opacity: 0.5; 740 } 741} 742 743/* ======================================== 744 Reduced Motion Override 745 ======================================== */ 746 747/* Respect user's motion preferences */ 748@media (prefers-reduced-motion: reduce) { 749 *, 750 *::before, 751 *::after { 752 animation-duration: 0.01ms !important; 753 animation-iteration-count: 1 !important; 754 transition-duration: 0.01ms !important; 755 } 756}