Atproto AMA app
at main 804 lines 18 kB view raw
1/* ============================================================================ 2 Design Tokens 3 ========================================================================= */ 4 5:root { 6 /* Spacing Scale - Fibonacci Sequence */ 7 --space-1: 1px; 8 --space-2: 2px; 9 --space-3: 3px; 10 --space-5: 5px; 11 --space-8: 8px; 12 --space-13: 13px; 13 --space-21: 21px; 14 --space-34: 34px; 15 --space-55: 55px; 16 --space-89: 89px; 17 18 /* Semantic Spacing Aliases */ 19 --space-xs: var(--space-3); 20 --space-sm: var(--space-8); 21 --space-md: var(--space-13); 22 --space-lg: var(--space-21); 23 --space-xl: var(--space-34); 24 --space-2xl: var(--space-55); 25 --space-3xl: var(--space-89); 26 27 /* Colors - Neutrals */ 28 --color-white: #ffffff; 29 --color-gray-50: #f9fafb; 30 --color-gray-100: #f3f4f6; 31 --color-gray-200: #e5e7eb; 32 --color-gray-300: #d1d5db; 33 --color-gray-400: #9ca3af; 34 --color-gray-500: #6b7280; 35 --color-gray-600: #4b5563; 36 --color-gray-700: #374151; 37 --color-gray-800: #1f2937; 38 --color-gray-900: #111827; 39 40 /* Colors - Primary (Blue) */ 41 --color-blue-50: #eff6ff; 42 --color-blue-100: #dbeafe; 43 --color-blue-500: #3b82f6; 44 --color-blue-600: #2563eb; 45 --color-blue-700: #1d4ed8; 46 47 /* Colors - Semantic (Success) */ 48 --color-green-50: #f0fdf4; 49 --color-green-100: #dcfce7; 50 --color-green-200: #bbf7d0; 51 --color-green-300: #86efac; 52 --color-green-500: #22c55e; 53 --color-green-600: #16a34a; 54 --color-green-700: #15803d; 55 --color-green-800: #166534; 56 57 /* Colors - Semantic (Danger/Error) */ 58 --color-red-50: #fef2f2; 59 --color-red-500: #ef4444; 60 --color-red-600: #dc2626; 61 62 /* Colors - Semantic (Warning) */ 63 --color-yellow-100: #fef3c7; 64 --color-yellow-800: #92400e; 65 66 /* Typography */ 67 --font-family-base: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; 68 --font-size-xs: 0.75rem; /* 12px */ 69 --font-size-sm: 0.875rem; /* 14px */ 70 --font-size-base: 1rem; /* 16px */ 71 --font-size-lg: 1.125rem; /* 18px */ 72 --font-size-xl: 1.25rem; /* 20px */ 73 --font-size-2xl: 1.5rem; /* 24px */ 74 --font-size-3xl: 1.875rem; /* 30px */ 75 --font-size-4xl: 2.25rem; /* 36px */ 76 77 --font-weight-normal: 400; 78 --font-weight-medium: 500; 79 --font-weight-semibold: 600; 80 --font-weight-bold: 700; 81 82 --line-height-tight: 1.1; 83 --line-height-normal: 1.35; 84 --line-height-relaxed: 1.5; 85 86 /* Border Radius */ 87 --radius-sm: 0.25rem; /* 4px */ 88 --radius-md: 0.375rem; /* 6px */ 89 --radius-lg: 0.5rem; /* 8px */ 90 --radius-full: 9999px; 91 92 /* Borders */ 93 --border-width: 1px; 94 --border-color: var(--color-gray-200); 95 --border: var(--border-width) solid var(--border-color); 96 97 /* Layout */ 98 --container-max-width: 42rem; /* max-w-2xl equivalent */ 99 --container-max-width-lg: 56rem; /* max-w-4xl equivalent */ 100 --container-padding: var(--space-md); 101 102 /* Transitions */ 103 --transition-base: 150ms ease-in-out; 104} 105 106/* ============================================================================ 107 Layer Declarations 108 ========================================================================= */ 109 110@layer base, components, utilities; 111 112/* ============================================================================ 113 Base Layer 114 ========================================================================= */ 115 116@layer base { 117 *, 118 *::before, 119 *::after { 120 box-sizing: border-box; 121 } 122 123 body { 124 margin: 0; 125 font-family: var(--font-family-base); 126 font-size: var(--font-size-base); 127 line-height: var(--line-height-relaxed); 128 color: var(--color-gray-900); 129 background-color: var(--color-gray-50); 130 } 131 132 a { 133 margin-right: 1rem; 134 } 135 136 main { 137 text-align: center; 138 padding: 1em; 139 margin: 0 auto; 140 } 141 142 /* Preserve existing h1 styles */ 143 h1 { 144 color: #335d92; 145 text-transform: uppercase; 146 font-size: 4rem; 147 font-weight: 100; 148 line-height: var(--line-height-tight); 149 margin: 4rem auto; 150 max-width: 14rem; 151 } 152 153 p { 154 max-width: 14rem; 155 margin: 2rem auto; 156 line-height: var(--line-height-normal); 157 } 158 159 @media (min-width: 480px) { 160 h1 { 161 max-width: none; 162 } 163 164 p { 165 max-width: none; 166 } 167 } 168} 169 170/* ============================================================================ 171 Components Layer 172 ========================================================================= */ 173 174@layer components { 175 176 /* App Root */ 177 .app-root { 178 min-height: 100vh; 179 background-color: var(--color-gray-50); 180 } 181 182 /* ------------------------------------------------------------------------- 183 Layout Components 184 ---------------------------------------------------------------------- */ 185 186 .container { 187 width: 100%; 188 max-width: var(--container-max-width); 189 margin-left: auto; 190 margin-right: auto; 191 padding: var(--container-padding); 192 } 193 194 .container--lg { 195 max-width: var(--container-max-width-lg); 196 } 197 198 .feed { 199 display: flex; 200 flex-direction: column; 201 gap: var(--space-md); 202 } 203 204 .feed--lg { 205 gap: var(--space-lg); 206 } 207 208 .feed__empty { 209 padding: var(--space-xl); 210 text-align: center; 211 color: var(--color-gray-500); 212 } 213 214 .stack { 215 display: flex; 216 flex-direction: column; 217 } 218 219 .stack--xs { gap: var(--space-xs); } 220 .stack--sm { gap: var(--space-sm); } 221 .stack--md { gap: var(--space-md); } 222 .stack--lg { gap: var(--space-lg); } 223 .stack--xl { gap: var(--space-xl); } 224 225 /* ------------------------------------------------------------------------- 226 Header Component 227 ---------------------------------------------------------------------- */ 228 229 .header { 230 background-color: var(--color-white); 231 border-bottom: var(--border); 232 } 233 234 .header__content { 235 max-width: var(--container-max-width); 236 margin: 0 auto; 237 padding: var(--space-md) var(--container-padding); 238 display: flex; 239 align-items: center; 240 justify-content: space-between; 241 } 242 243 .header__brand { 244 font-size: var(--font-size-xl); 245 font-weight: var(--font-weight-bold); 246 color: var(--color-gray-900); 247 text-decoration: none; 248 margin-right: 0; 249 } 250 251 .header__brand:hover { 252 color: var(--color-gray-700); 253 } 254 255 .header__nav { 256 display: flex; 257 align-items: center; 258 gap: var(--space-md); 259 } 260 261 .header__user { 262 display: flex; 263 align-items: center; 264 gap: var(--space-sm); 265 text-decoration: none; 266 margin-right: 0; 267 } 268 269 .header__user:hover { 270 opacity: 0.8; 271 } 272 273 .header__handle { 274 font-size: var(--font-size-sm); 275 color: var(--color-gray-600); 276 } 277 278 /* ------------------------------------------------------------------------- 279 Avatar Component 280 ---------------------------------------------------------------------- */ 281 282 .avatar { 283 display: inline-flex; 284 align-items: center; 285 justify-content: center; 286 border-radius: var(--radius-full); 287 object-fit: cover; 288 font-weight: var(--font-weight-medium); 289 overflow: hidden; 290 flex-shrink: 0; 291 } 292 293 .avatar--sm { 294 width: var(--space-lg); 295 height: var(--space-lg); 296 font-size: var(--font-size-xs); 297 } 298 299 .avatar--md { 300 width: var(--space-xl); 301 height: var(--space-xl); 302 font-size: var(--font-size-sm); 303 } 304 305 .avatar--lg { 306 width: 3rem; 307 height: 3rem; 308 font-size: var(--font-size-base); 309 } 310 311 .avatar__fallback { 312 background-color: var(--color-gray-200); 313 color: var(--color-gray-600); 314 } 315 316 /* ------------------------------------------------------------------------- 317 QuestionCard Component 318 ---------------------------------------------------------------------- */ 319 320 .question-card { 321 background-color: var(--color-white); 322 border: var(--border); 323 border-radius: var(--radius-lg); 324 padding: var(--space-md); 325 } 326 327 .question-card__section { 328 display: flex; 329 flex-direction: column; 330 gap: var(--space-sm); 331 } 332 333 .question-card__section + .question-card__section { 334 margin-top: var(--space-md); 335 padding-top: var(--space-md); 336 border-top: var(--border); 337 } 338 339 .question-card__metadata { 340 display: flex; 341 align-items: center; 342 gap: var(--space-sm); 343 font-size: var(--font-size-sm); 344 color: var(--color-gray-500); 345 } 346 347 .question-card__metadata-name { 348 font-weight: var(--font-weight-medium); 349 color: var(--color-gray-700); 350 } 351 352 .question-card__metadata-link { 353 font-weight: var(--font-weight-medium); 354 color: var(--color-blue-600); 355 text-decoration: none; 356 margin-right: 0; 357 } 358 359 .question-card__metadata-link:hover { 360 text-decoration: underline; 361 } 362 363 .question-card__metadata-date { 364 color: var(--color-gray-400); 365 } 366 367 .question-card__content { 368 color: var(--color-gray-800); 369 padding-left: var(--space-xl); 370 } 371 372 /* Success variant for answered questions */ 373 .question-card--success { 374 border-color: var(--color-green-300); 375 background-color: var(--color-green-50); 376 position: relative; 377 overflow: hidden; 378 } 379 380 .question-card__success-header { 381 display: flex; 382 align-items: center; 383 justify-content: space-between; 384 margin-bottom: var(--space-md); 385 } 386 387 .question-card__success-icon { 388 display: flex; 389 align-items: center; 390 gap: var(--space-sm); 391 color: var(--color-green-700); 392 font-weight: var(--font-weight-medium); 393 font-size: var(--font-size-sm); 394 } 395 396 .question-card__success-icon svg { 397 width: var(--space-lg); 398 height: var(--space-lg); 399 } 400 401 .question-card__delete-btn { 402 padding: 0; 403 background: none; 404 border: none; 405 color: var(--color-gray-600); 406 cursor: pointer; 407 display: flex; 408 align-items: center; 409 justify-content: center; 410 } 411 412 .question-card__delete-btn:hover { 413 color: var(--color-gray-800); 414 } 415 416 .question-card__delete-btn svg { 417 width: var(--space-lg); 418 height: var(--space-lg); 419 } 420 421 .question-card__progress-bar { 422 position: absolute; 423 bottom: 0; 424 left: 0; 425 right: 0; 426 height: var(--space-2); 427 background-color: var(--color-green-200); 428 } 429 430 .question-card__progress-fill { 431 height: 100%; 432 background-color: var(--color-green-500); 433 animation: shrink-width 5s linear forwards; 434 } 435 436 .question-card__answer-form { 437 display: flex; 438 flex-direction: column; 439 gap: var(--space-sm); 440 margin-top: var(--space-md); 441 padding-top: var(--space-md); 442 border-top: var(--border); 443 } 444 445 .question-card__answer-actions { 446 display: flex; 447 align-items: center; 448 justify-content: space-between; 449 } 450 451 .question-card__answer-meta { 452 font-size: var(--font-size-xs); 453 color: var(--color-gray-400); 454 } 455 456 /* ------------------------------------------------------------------------- 457 Form Components 458 ---------------------------------------------------------------------- */ 459 460 .form-group { 461 display: flex; 462 flex-direction: column; 463 gap: var(--space-xs); 464 } 465 466 .form-label { 467 display: block; 468 font-size: var(--font-size-sm); 469 font-weight: var(--font-weight-medium); 470 color: var(--color-gray-700); 471 } 472 473 .form-input, 474 .form-textarea { 475 width: 100%; 476 padding: var(--space-sm) var(--space-md); 477 border: var(--border); 478 border-color: var(--color-gray-300); 479 border-radius: var(--radius-md); 480 font-size: var(--font-size-sm); 481 line-height: var(--line-height-normal); 482 transition: all var(--transition-base); 483 font-family: var(--font-family-base); 484 } 485 486 .form-input:focus, 487 .form-textarea:focus { 488 outline: none; 489 border-color: var(--color-blue-500); 490 box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); 491 } 492 493 .form-hint { 494 font-size: var(--font-size-sm); 495 color: var(--color-gray-500); 496 margin-top: var(--space-xs); 497 } 498 499 /* ------------------------------------------------------------------------- 500 Button Component 501 ---------------------------------------------------------------------- */ 502 503 .btn { 504 display: inline-flex; 505 align-items: center; 506 justify-content: center; 507 padding: var(--space-sm) var(--space-md); 508 font-size: var(--font-size-sm); 509 font-weight: var(--font-weight-medium); 510 border-radius: var(--radius-md); 511 border: none; 512 cursor: pointer; 513 transition: all var(--transition-base); 514 text-decoration: none; 515 margin-right: 0; 516 } 517 518 .btn:focus { 519 outline: none; 520 box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); 521 } 522 523 .btn:disabled { 524 opacity: 0.5; 525 cursor: not-allowed; 526 } 527 528 /* Button Variants */ 529 .btn--primary { 530 background-color: var(--color-blue-500); 531 color: var(--color-white); 532 } 533 534 .btn--primary:hover:not(:disabled) { 535 background-color: var(--color-blue-600); 536 } 537 538 .btn--secondary { 539 background-color: transparent; 540 color: var(--color-gray-500); 541 padding: var(--space-sm) var(--space-md); 542 } 543 544 .btn--secondary:hover:not(:disabled) { 545 color: var(--color-gray-700); 546 } 547 548 .btn--success { 549 background-color: var(--color-green-500); 550 color: var(--color-white); 551 } 552 553 .btn--success:hover:not(:disabled) { 554 background-color: var(--color-green-600); 555 } 556 557 .btn--danger { 558 background-color: var(--color-red-500); 559 color: var(--color-white); 560 } 561 562 .btn--danger:hover:not(:disabled) { 563 background-color: var(--color-red-600); 564 } 565 566 /* Button Sizes */ 567 .btn--sm { 568 padding: var(--space-xs) var(--space-md); 569 font-size: var(--font-size-xs); 570 } 571 572 .btn--full { 573 width: 100%; 574 } 575 576 /* ------------------------------------------------------------------------- 577 Card Component 578 ---------------------------------------------------------------------- */ 579 580 .card { 581 background-color: var(--color-white); 582 border: var(--border); 583 border-radius: var(--radius-lg); 584 padding: var(--space-lg); 585 } 586 587 .card--center { 588 text-align: center; 589 } 590 591 /* ------------------------------------------------------------------------- 592 Typography Components 593 ---------------------------------------------------------------------- */ 594 595 .page-title { 596 font-size: var(--font-size-2xl); 597 font-weight: var(--font-weight-bold); 598 color: var(--color-gray-900); 599 margin-bottom: var(--space-xs); 600 } 601 602 .page-subtitle { 603 font-size: var(--font-size-sm); 604 color: var(--color-gray-600); 605 } 606 607 .section-title { 608 font-size: var(--font-size-lg); 609 font-weight: var(--font-weight-semibold); 610 color: var(--color-gray-700); 611 } 612 613 .text-link { 614 color: var(--color-blue-600); 615 text-decoration: none; 616 margin-right: 0; 617 } 618 619 .text-link:hover { 620 text-decoration: underline; 621 } 622 623 .text-link--back { 624 font-size: var(--font-size-sm); 625 } 626 627 .text-link--inline { 628 color: var(--color-blue-500); 629 } 630 631 .text-muted { 632 color: var(--color-gray-600); 633 } 634 635 .text-sm { 636 font-size: var(--font-size-sm); 637 } 638 639 .text-xs { 640 font-size: var(--font-size-xs); 641 } 642 643 .text-center { 644 text-align: center; 645 } 646 647 /* ------------------------------------------------------------------------- 648 Status Badge Component 649 ---------------------------------------------------------------------- */ 650 651 .status-badge { 652 display: inline-flex; 653 align-items: center; 654 gap: var(--space-sm); 655 padding: var(--space-xs) var(--space-md); 656 border-radius: var(--radius-full); 657 font-size: var(--font-size-sm); 658 font-weight: var(--font-weight-medium); 659 } 660 661 .status-badge--open, 662 .status-badge--success { 663 background-color: var(--color-green-100); 664 color: var(--color-green-800); 665 } 666 667 .status-badge--closed { 668 background-color: var(--color-gray-100); 669 color: var(--color-gray-800); 670 } 671 672 .status-badge--pending, 673 .status-badge--warning { 674 background-color: var(--color-yellow-100); 675 color: var(--color-yellow-800); 676 } 677 678 /* ------------------------------------------------------------------------- 679 Thread/AMA Components 680 ---------------------------------------------------------------------- */ 681 682 .ama-header { 683 display: flex; 684 align-items: center; 685 justify-content: space-between; 686 margin-bottom: var(--space-md); 687 } 688 689 .ama-actions { 690 display: flex; 691 gap: var(--space-sm); 692 } 693 694 .thread-item { 695 background-color: var(--color-white); 696 border: var(--border); 697 border-radius: var(--radius-lg); 698 padding: var(--space-md); 699 } 700 701 .thread-item__header { 702 display: flex; 703 align-items: start; 704 justify-content: space-between; 705 margin-bottom: var(--space-sm); 706 } 707 708 .thread-item__content { 709 flex: 1; 710 } 711 712 .thread-item__meta { 713 display: flex; 714 flex-direction: column; 715 gap: var(--space-xs); 716 } 717 718 .thread-item__asker { 719 font-size: var(--font-size-sm); 720 color: var(--color-gray-600); 721 } 722 723 .thread-item__date { 724 font-size: var(--font-size-sm); 725 color: var(--color-gray-500); 726 } 727 728 .thread-item__status-badge { 729 padding: var(--space-xs) var(--space-sm); 730 border-radius: var(--radius-sm); 731 font-size: var(--font-size-xs); 732 font-weight: var(--font-weight-medium); 733 } 734 735 /* ------------------------------------------------------------------------- 736 Utility Classes 737 ---------------------------------------------------------------------- */ 738 739 .error-text { 740 color: var(--color-red-500); 741 font-size: var(--font-size-sm); 742 } 743 744 .flex { 745 display: flex; 746 } 747 748 .flex-col { 749 flex-direction: column; 750 } 751 752 .items-center { 753 align-items: center; 754 } 755 756 .items-start { 757 align-items: flex-start; 758 } 759 760 .justify-between { 761 justify-content: space-between; 762 } 763 764 .gap-2 { 765 gap: var(--space-sm); 766 } 767 768 .gap-3 { 769 gap: var(--space-md); 770 } 771 772 .gap-4 { 773 gap: var(--space-lg); 774 } 775 776 .mb-4 { 777 margin-bottom: var(--space-md); 778 } 779 780 .mt-1 { 781 margin-top: var(--space-xs); 782 } 783 784 .block { 785 display: block; 786 } 787 788 .inline-block { 789 display: inline-block; 790 } 791} 792 793/* ============================================================================ 794 Animations 795 ========================================================================= */ 796 797@keyframes shrink-width { 798 from { 799 width: 100%; 800 } 801 to { 802 width: 0%; 803 } 804}