Coffee journaling on ATProto (alpha)
alpha.arabica.social
coffee
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}