Our Personal Data Server from scratch! tranquil.farm
atproto pds rust postgresql fun oauth

refactor(frontend): extract oauth primary flow page styles #62

merged opened by oyster.cafe targeting main from refactor/extract-scoped-styles
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:3fwecdnvtcscjnrx2p4n7alz/sh.tangled.repo.pull/3mhdc5fbusg22
+16 -892
Diff #0
+3 -397
frontend/src/routes/OAuthConsent.svelte
··· 49 49 } 50 50 51 51 let loading = $state(true) 52 - let showSpinner = $state(false) 53 - let loadingTimer: ReturnType<typeof setTimeout> | null = null 54 52 let error = $state<string | null>(null) 55 53 let submitting = $state(false) 56 54 let consentData = $state<ConsentData | null>(null) ··· 140 138 error = $_('oauth.error.genericError') 141 139 } finally { 142 140 loading = false 143 - showSpinner = false 144 - if (loadingTimer) { 145 - clearTimeout(loadingTimer) 146 - loadingTimer = null 147 - } 148 141 } 149 142 } 150 143 ··· 252 245 } 253 246 254 247 $effect(() => { 255 - loadingTimer = setTimeout(() => { 256 - if (loading) { 257 - showSpinner = true 258 - } 259 - }, 5000) 260 248 fetchConsentData() 261 - return () => { 262 - if (loadingTimer) { 263 - clearTimeout(loadingTimer) 264 - } 265 - } 266 249 }) 267 250 268 251 let scopeGroups = $derived(consentData ? groupScopesByCategory(consentData.scopes) : []) ··· 297 280 298 281 <div class="consent-container"> 299 282 {#if loading} 300 - <div class="loading"> 301 - {#if showSpinner} 302 - <div class="loading-content"> 303 - <div class="spinner"></div> 304 - <p>{$_('common.loading')}</p> 305 - </div> 306 - {:else} 307 - <p style="color: var(--text-muted); font-size: 0.875rem;">Loading consent data...</p> 308 - {/if} 309 - </div> 283 + <div class="loading"></div> 310 284 {:else if error} 311 285 <div class="error-container"> 312 286 <h1>{$_('oauth.error.title')}</h1> ··· 421 395 </div> 422 396 423 397 <div class="actions"> 424 - <button type="button" class="deny-btn" onclick={handleDeny} disabled={submitting}> 398 + <button type="button" class="cancel" onclick={handleDeny} disabled={submitting}> 425 399 {$_('oauth.consent.deny')} 426 400 </button> 427 - <button type="button" class="approve-btn" onclick={submitConsent} disabled={submitting}> 401 + <button type="button" onclick={submitConsent} disabled={submitting}> 428 402 {submitting ? $_('oauth.consent.authorizing') : $_('oauth.consent.authorize')} 429 403 </button> 430 404 </div> ··· 443 417 </div> 444 418 {/if} 445 419 </div> 446 - 447 - <style> 448 - .consent-container { 449 - max-width: var(--width-lg); 450 - margin: var(--space-7) auto; 451 - padding: var(--space-7); 452 - } 453 - 454 - .loading { 455 - display: flex; 456 - align-items: center; 457 - justify-content: center; 458 - min-height: 200px; 459 - color: var(--text-secondary); 460 - } 461 - 462 - .loading-content { 463 - display: flex; 464 - flex-direction: column; 465 - align-items: center; 466 - gap: var(--space-4); 467 - } 468 - 469 - .loading-content p { 470 - margin: 0; 471 - color: var(--text-secondary); 472 - } 473 - 474 - .error-container { 475 - text-align: center; 476 - max-width: var(--width-sm); 477 - margin: 0 auto; 478 - } 479 - 480 - .error { 481 - padding: var(--space-3); 482 - background: var(--error-bg); 483 - border: 1px solid var(--error-border); 484 - border-radius: var(--radius-md); 485 - color: var(--error-text); 486 - margin-bottom: var(--space-4); 487 - } 488 - 489 - .client-panel { 490 - display: flex; 491 - flex-direction: column; 492 - gap: var(--space-5); 493 - } 494 - 495 - .permissions-panel { 496 - min-width: 0; 497 - } 498 - 499 - .client-info { 500 - text-align: center; 501 - padding: var(--space-6); 502 - background: var(--bg-secondary); 503 - border-radius: var(--radius-xl); 504 - } 505 - 506 - @media (min-width: 800px) { 507 - .client-info { 508 - text-align: left; 509 - } 510 - } 511 - 512 - .client-logo { 513 - width: 64px; 514 - height: 64px; 515 - border-radius: var(--radius-xl); 516 - margin-bottom: var(--space-4); 517 - } 518 - 519 - .client-info h1 { 520 - margin: 0 0 var(--space-1) 0; 521 - font-size: var(--text-xl); 522 - } 523 - 524 - .subtitle { 525 - color: var(--text-secondary); 526 - margin: 0; 527 - } 528 - 529 - .client-link { 530 - display: inline-block; 531 - margin-top: var(--space-2); 532 - font-size: var(--text-sm); 533 - color: var(--accent); 534 - text-decoration: none; 535 - } 536 - 537 - .client-link:hover { 538 - text-decoration: underline; 539 - } 540 - 541 - .account-info { 542 - display: flex; 543 - flex-direction: column; 544 - gap: var(--space-1); 545 - padding: var(--space-4); 546 - background: var(--bg-secondary); 547 - border-radius: var(--radius-xl); 548 - margin-bottom: var(--space-6); 549 - } 550 - 551 - .account-info .label { 552 - font-size: var(--text-xs); 553 - color: var(--text-muted); 554 - text-transform: uppercase; 555 - letter-spacing: 0.05em; 556 - } 557 - 558 - .account-info .did { 559 - font-family: var(--font-mono); 560 - font-size: var(--text-sm); 561 - color: var(--text-secondary); 562 - word-break: break-all; 563 - } 564 - 565 - .account-info .handle { 566 - font-size: var(--text-base); 567 - font-weight: var(--font-medium); 568 - color: var(--text-primary); 569 - } 570 - 571 - .delegation-badge { 572 - display: inline-block; 573 - padding: var(--space-1) var(--space-2); 574 - background: var(--accent); 575 - color: var(--text-inverse); 576 - border-radius: var(--radius-md); 577 - font-size: var(--text-xs); 578 - font-weight: var(--font-semibold); 579 - text-transform: uppercase; 580 - letter-spacing: 0.05em; 581 - margin-bottom: var(--space-3); 582 - } 583 - 584 - .delegation-info { 585 - display: flex; 586 - flex-direction: column; 587 - gap: var(--space-2); 588 - } 589 - 590 - .delegation-info .info-row { 591 - display: flex; 592 - flex-direction: column; 593 - gap: 2px; 594 - } 595 - 596 - .delegation-info .handle { 597 - font-weight: var(--font-medium); 598 - color: var(--text-primary); 599 - } 600 - 601 - .level-badge { 602 - display: inline-block; 603 - padding: 2px var(--space-2); 604 - background: var(--bg-tertiary); 605 - color: var(--text-primary); 606 - border-radius: var(--radius-sm); 607 - font-size: var(--text-sm); 608 - font-weight: var(--font-medium); 609 - } 610 - 611 - .level-badge.level-owner { 612 - background: var(--success-bg); 613 - color: var(--success-text); 614 - } 615 - 616 - .level-badge.level-admin { 617 - background: var(--accent); 618 - color: var(--text-inverse); 619 - } 620 - 621 - .level-badge.level-editor { 622 - background: var(--warning-bg); 623 - color: var(--warning-text); 624 - } 625 - 626 - .level-badge.level-viewer { 627 - background: var(--bg-tertiary); 628 - color: var(--text-secondary); 629 - } 630 - 631 - .permissions-notice { 632 - margin-top: var(--space-3); 633 - padding: var(--space-3); 634 - background: var(--warning-bg); 635 - border: 1px solid var(--warning-border); 636 - border-radius: var(--radius-md); 637 - } 638 - 639 - .notice-header { 640 - display: flex; 641 - align-items: center; 642 - gap: var(--space-2); 643 - font-weight: var(--font-semibold); 644 - color: var(--warning-text); 645 - margin-bottom: var(--space-2); 646 - } 647 - 648 - .notice-header svg { 649 - flex-shrink: 0; 650 - } 651 - 652 - .notice-text { 653 - margin: 0; 654 - font-size: var(--text-sm); 655 - color: var(--warning-text); 656 - line-height: 1.5; 657 - } 658 - 659 - .scopes-section { 660 - margin-bottom: var(--space-6); 661 - } 662 - 663 - .scopes-section h2 { 664 - font-size: var(--text-base); 665 - margin: 0 0 var(--space-4) 0; 666 - color: var(--text-secondary); 667 - } 668 - 669 - .scope-group { 670 - margin-bottom: var(--space-4); 671 - } 672 - 673 - .category-title { 674 - font-size: var(--text-sm); 675 - font-weight: var(--font-semibold); 676 - color: var(--text-primary); 677 - margin: 0 0 var(--space-2) 0; 678 - padding-bottom: var(--space-1); 679 - border-bottom: 1px solid var(--border-color); 680 - } 681 - 682 - .scope-item { 683 - display: flex; 684 - gap: var(--space-3); 685 - padding: var(--space-3); 686 - background: var(--bg-card); 687 - border: 1px solid var(--border-color); 688 - border-radius: var(--radius-lg); 689 - margin-bottom: var(--space-2); 690 - cursor: pointer; 691 - transition: border-color var(--transition-fast); 692 - overflow: hidden; 693 - } 694 - 695 - .scope-item:hover:not(.required) { 696 - border-color: var(--accent); 697 - } 698 - 699 - .scope-item.required { 700 - background: var(--bg-secondary); 701 - } 702 - 703 - .scope-item.read-only { 704 - background: var(--bg-secondary); 705 - border-style: dashed; 706 - } 707 - 708 - .scope-item input[type="checkbox"] { 709 - flex-shrink: 0; 710 - width: 18px; 711 - height: 18px; 712 - margin-top: 2px; 713 - } 714 - 715 - .scope-info { 716 - flex: 1; 717 - min-width: 0; 718 - display: flex; 719 - flex-direction: column; 720 - gap: 2px; 721 - overflow: hidden; 722 - } 723 - 724 - .scope-name { 725 - font-weight: var(--font-medium); 726 - color: var(--text-primary); 727 - word-break: break-all; 728 - } 729 - 730 - .scope-description { 731 - font-size: var(--text-sm); 732 - color: var(--text-secondary); 733 - word-break: break-all; 734 - } 735 - 736 - .required-badge { 737 - display: inline-block; 738 - font-size: 0.625rem; 739 - padding: 2px var(--space-2); 740 - background: var(--warning-bg); 741 - color: var(--warning-text); 742 - border-radius: var(--radius-sm); 743 - text-transform: uppercase; 744 - letter-spacing: 0.05em; 745 - margin-top: var(--space-1); 746 - width: fit-content; 747 - } 748 - 749 - .remember-choice { 750 - display: flex; 751 - align-items: center; 752 - gap: var(--space-2); 753 - margin-top: var(--space-5); 754 - cursor: pointer; 755 - color: var(--text-secondary); 756 - font-size: var(--text-sm); 757 - } 758 - 759 - .remember-choice input { 760 - width: 16px; 761 - height: 16px; 762 - } 763 - 764 - .actions { 765 - display: flex; 766 - gap: var(--space-4); 767 - margin-top: var(--space-6); 768 - } 769 - 770 - @media (min-width: 800px) { 771 - .actions { 772 - max-width: 400px; 773 - margin-left: auto; 774 - } 775 - } 776 - 777 - .actions button { 778 - flex: 1; 779 - padding: var(--space-3); 780 - border: none; 781 - border-radius: var(--radius-lg); 782 - font-size: var(--text-base); 783 - font-weight: var(--font-medium); 784 - cursor: pointer; 785 - transition: background-color var(--transition-fast); 786 - } 787 - 788 - .actions button:disabled { 789 - opacity: 0.6; 790 - cursor: not-allowed; 791 - } 792 - 793 - .deny-btn { 794 - background: var(--bg-secondary); 795 - color: var(--text-primary); 796 - border: 1px solid var(--border-color); 797 - } 798 - 799 - .deny-btn:hover:not(:disabled) { 800 - background: var(--error-bg); 801 - border-color: var(--error-border); 802 - color: var(--error-text); 803 - } 804 - 805 - .approve-btn { 806 - background: var(--accent); 807 - color: var(--text-inverse); 808 - } 809 - 810 - .approve-btn:hover:not(:disabled) { 811 - background: var(--accent-hover); 812 - } 813 - </style>
+8 -291
frontend/src/routes/OAuthLogin.svelte
··· 401 401 {/if} 402 402 403 403 <form onsubmit={handleSubmit}> 404 - <div class="field"> 404 + <div> 405 405 <label for="username">{$_('register.handle')}</label> 406 406 <input 407 407 id="username" ··· 425 425 disabled={submitting || ssoLoading !== null} 426 426 > 427 427 {#if ssoLoading === provider.provider} 428 - <span class="spinner sm"></span> 428 + <span>{$_('common.loading')}</span> 429 429 {:else} 430 430 <SsoIcon provider={provider.icon} size={20} /> 431 431 {/if} ··· 445 445 <h3>{$_('oauth.login.signInWithPasskey')}</h3> 446 446 <button 447 447 type="button" 448 - class="passkey-btn" 448 + style="width: 100%" 449 449 class:passkey-unavailable={!hasPasskeys || checkingSecurityStatus || !securityStatusChecked} 450 450 onclick={handlePasskeyLogin} 451 451 disabled={submitting || !hasPasskeys || !username || checkingSecurityStatus || !securityStatusChecked} ··· 494 494 <span>{$_('oauth.login.rememberDevice')}</span> 495 495 </label> 496 496 497 - <button type="submit" class="submit-btn" disabled={submitting || !username || !password}> 497 + <button type="submit" disabled={submitting || !username || !password}> 498 498 {submitting ? $_('oauth.login.signingIn') : $_('oauth.login.title')} 499 499 </button> 500 500 </div> ··· 502 502 </div> 503 503 504 504 <div class="cancel-row"> 505 - <button type="button" class="cancel-btn-subtle" onclick={handleCancel} disabled={submitting}> 505 + <button type="button" class="ghost sm" onclick={handleCancel} disabled={submitting}> 506 506 {$_('common.cancel')} 507 507 </button> 508 508 </div> 509 509 {:else} 510 510 {#if hasPassword || !securityStatusChecked} 511 - <div class="field"> 511 + <div> 512 512 <label for="password">{$_('oauth.login.password')}</label> 513 513 <input 514 514 id="password" ··· 526 526 </label> 527 527 528 528 <div class="actions"> 529 - <button type="submit" class="submit-btn" disabled={submitting || !username || !password}> 529 + <button type="submit" disabled={submitting || !username || !password}> 530 530 {submitting ? $_('oauth.login.signingIn') : $_('oauth.login.title')} 531 531 </button> 532 532 </div> 533 533 {/if} 534 534 535 535 <div class="cancel-row"> 536 - <button type="button" class="cancel-btn-subtle" onclick={handleCancel} disabled={submitting}> 536 + <button type="button" class="ghost sm" onclick={handleCancel} disabled={submitting}> 537 537 {$_('common.cancel')} 538 538 </button> 539 539 </div> ··· 544 544 <a href={getFullUrl(routes.resetPassword)}>{$_('login.forgotPassword')}</a> &middot; <a href={getFullUrl(routes.requestPasskeyRecovery)}>{$_('login.lostPasskey')}</a> 545 545 </p> 546 546 </div> 547 - 548 - <style> 549 - .help-links { 550 - text-align: center; 551 - margin-top: var(--space-4); 552 - font-size: var(--text-sm); 553 - } 554 - 555 - .help-links a { 556 - color: var(--accent); 557 - text-decoration: none; 558 - } 559 - 560 - .help-links a:hover { 561 - text-decoration: underline; 562 - } 563 - 564 - form { 565 - display: flex; 566 - flex-direction: column; 567 - gap: var(--space-4); 568 - } 569 - 570 - .auth-methods { 571 - display: grid; 572 - grid-template-columns: 1fr; 573 - gap: var(--space-5); 574 - margin-top: var(--space-4); 575 - } 576 - 577 - @media (min-width: 600px) { 578 - .auth-methods { 579 - grid-template-columns: 1fr auto 1fr; 580 - align-items: start; 581 - } 582 - } 583 - 584 - .auth-methods.single-method { 585 - grid-template-columns: 1fr; 586 - } 587 - 588 - @media (min-width: 600px) { 589 - .auth-methods.single-method { 590 - grid-template-columns: 1fr; 591 - max-width: 400px; 592 - margin: var(--space-4) auto 0; 593 - } 594 - } 595 - 596 - .passkey-method, 597 - .password-method { 598 - display: flex; 599 - flex-direction: column; 600 - gap: var(--space-4); 601 - padding: var(--space-5); 602 - background: var(--bg-secondary); 603 - border-radius: var(--radius-xl); 604 - } 605 - 606 - .passkey-method h3, 607 - .password-method h3 { 608 - margin: 0; 609 - font-size: var(--text-sm); 610 - font-weight: var(--font-semibold); 611 - color: var(--text-secondary); 612 - text-transform: uppercase; 613 - letter-spacing: 0.05em; 614 - } 615 - 616 - 617 - .method-divider { 618 - display: flex; 619 - align-items: center; 620 - justify-content: center; 621 - color: var(--text-muted); 622 - font-size: var(--text-sm); 623 - } 624 - 625 - @media (min-width: 600px) { 626 - .method-divider { 627 - flex-direction: column; 628 - padding: 0 var(--space-3); 629 - } 630 - 631 - .method-divider::before, 632 - .method-divider::after { 633 - content: ''; 634 - width: 1px; 635 - height: var(--space-6); 636 - background: var(--border-color); 637 - } 638 - 639 - .method-divider span { 640 - writing-mode: vertical-rl; 641 - text-orientation: mixed; 642 - transform: rotate(180deg); 643 - padding: var(--space-2) 0; 644 - } 645 - } 646 - 647 - @media (max-width: 599px) { 648 - .method-divider { 649 - gap: var(--space-4); 650 - } 651 - 652 - .method-divider::before, 653 - .method-divider::after { 654 - content: ''; 655 - flex: 1; 656 - height: 1px; 657 - background: var(--border-color); 658 - } 659 - } 660 - 661 - .remember-device { 662 - display: flex; 663 - align-items: center; 664 - gap: var(--space-2); 665 - cursor: pointer; 666 - color: var(--text-secondary); 667 - font-size: var(--text-sm); 668 - } 669 - 670 - .remember-device input { 671 - width: 16px; 672 - height: 16px; 673 - } 674 - 675 - .actions { 676 - display: flex; 677 - gap: var(--space-4); 678 - margin-top: var(--space-2); 679 - } 680 - 681 - .actions button { 682 - flex: 1; 683 - } 684 - 685 - .cancel-row { 686 - display: flex; 687 - justify-content: center; 688 - margin-top: var(--space-4); 689 - } 690 - 691 - .cancel-btn-subtle { 692 - padding: var(--space-2) var(--space-4); 693 - background: transparent; 694 - color: var(--text-muted); 695 - border: none; 696 - border-radius: var(--radius-md); 697 - font-size: var(--text-sm); 698 - cursor: pointer; 699 - transition: color var(--transition-fast); 700 - } 701 - 702 - .cancel-btn-subtle:hover:not(:disabled) { 703 - color: var(--text-secondary); 704 - } 705 - 706 - .cancel-btn-subtle:disabled { 707 - opacity: 0.6; 708 - cursor: not-allowed; 709 - } 710 - 711 - .submit-btn { 712 - background: var(--accent); 713 - color: var(--text-inverse); 714 - } 715 - 716 - .submit-btn:hover:not(:disabled) { 717 - background: var(--accent-hover); 718 - } 719 - 720 - .passkey-btn { 721 - display: flex; 722 - align-items: center; 723 - justify-content: center; 724 - gap: var(--space-2); 725 - width: 100%; 726 - padding: var(--space-3); 727 - background: var(--accent); 728 - color: var(--text-inverse); 729 - border: 1px solid var(--accent); 730 - border-radius: var(--radius-md); 731 - font-size: var(--text-base); 732 - cursor: pointer; 733 - transition: background-color var(--transition-fast), border-color var(--transition-fast), opacity var(--transition-fast); 734 - } 735 - 736 - .passkey-btn:hover:not(:disabled) { 737 - background: var(--accent-hover); 738 - border-color: var(--accent-hover); 739 - } 740 - 741 - .passkey-btn:disabled { 742 - opacity: 0.6; 743 - cursor: not-allowed; 744 - } 745 - 746 - .passkey-btn.passkey-unavailable { 747 - background: var(--bg-secondary); 748 - color: var(--text-secondary); 749 - border-color: var(--border-color); 750 - } 751 - 752 - .passkey-icon { 753 - width: 20px; 754 - height: 20px; 755 - } 756 - 757 - .passkey-text { 758 - flex: 1; 759 - text-align: left; 760 - } 761 - 762 - .sso-section { 763 - margin-top: var(--space-6); 764 - } 765 - 766 - .sso-section-top { 767 - margin-top: var(--space-4); 768 - margin-bottom: 0; 769 - } 770 - 771 - .sso-section-top .sso-divider { 772 - margin-top: var(--space-5); 773 - margin-bottom: 0; 774 - } 775 - 776 - .sso-divider { 777 - display: flex; 778 - align-items: center; 779 - gap: var(--space-4); 780 - margin-bottom: var(--space-4); 781 - color: var(--text-muted); 782 - font-size: var(--text-sm); 783 - } 784 - 785 - .sso-divider::before, 786 - .sso-divider::after { 787 - content: ''; 788 - flex: 1; 789 - height: 1px; 790 - background: var(--border-color); 791 - } 792 - 793 - .sso-buttons { 794 - display: flex; 795 - flex-wrap: wrap; 796 - gap: var(--space-3); 797 - justify-content: center; 798 - } 799 - 800 - .sso-btn { 801 - display: flex; 802 - align-items: center; 803 - gap: var(--space-2); 804 - padding: var(--space-2) var(--space-4); 805 - background: var(--bg-secondary); 806 - color: var(--text-primary); 807 - border: 1px solid var(--border-color); 808 - border-radius: var(--radius-md); 809 - font-size: var(--text-sm); 810 - cursor: pointer; 811 - transition: background-color var(--transition-fast), border-color var(--transition-fast); 812 - } 813 - 814 - .sso-btn-prominent { 815 - padding: var(--space-3) var(--space-5); 816 - font-size: var(--text-base); 817 - font-weight: var(--font-medium); 818 - } 819 - 820 - .sso-btn:hover:not(:disabled) { 821 - background: var(--bg-tertiary); 822 - border-color: var(--accent); 823 - } 824 - 825 - .sso-btn:disabled { 826 - opacity: 0.6; 827 - cursor: not-allowed; 828 - } 829 - </style>
+5 -204
frontend/src/routes/OAuthRegister.svelte
··· 308 308 309 309 <div class="oauth-register-container"> 310 310 {#if loadingServerInfo} 311 - <div class="loading"> 312 - <div class="spinner"></div> 313 - <p>{$_('common.loading')}</p> 314 - </div> 311 + <div class="loading"></div> 315 312 {:else if flow} 316 313 <header class="page-header"> 317 314 <h1>{$_('oauth.register.title')}</h1> ··· 345 342 <div class="split-layout"> 346 343 <div class="form-section"> 347 344 <form onsubmit={handleInfoSubmit}> 348 - <div class="field"> 345 + <div> 349 346 <label for="handle">{$_('register.handle')}</label> 350 347 <HandleInput 351 348 value={flow.info.handle} ··· 484 481 </fieldset> 485 482 486 483 {#if serverInfo?.inviteCodeRequired} 487 - <div class="field"> 484 + <div> 488 485 <label for="invite-code">{$_('register.inviteCode')} <span class="required">*</span></label> 489 486 <input 490 487 id="invite-code" ··· 504 501 </div> 505 502 506 503 <div class="secondary-actions"> 507 - <button type="button" class="link-btn" onclick={goToLogin}> 504 + <button type="button" class="link" onclick={goToLogin}> 508 505 {$_('oauth.register.haveAccount')} 509 506 </button> 510 - <button type="button" class="link-btn" onclick={handleCancel}> 507 + <button type="button" class="link" onclick={handleCancel}> 511 508 {$_('common.cancel')} 512 509 </button> 513 510 </div> ··· 540 537 541 538 {:else if flow.state.step === 'creating'} 542 539 <div class="creating"> 543 - <div class="spinner"></div> 544 540 <p>{$_('registerPasskey.creatingAccount')}</p> 545 541 </div> 546 542 ··· 582 578 583 579 {:else if flow.state.step === 'activating'} 584 580 <div class="creating"> 585 - <div class="spinner"></div> 586 581 <p>{$_('registerPasskey.activatingAccount')}</p> 587 582 </div> 588 583 {/if} 589 584 {/if} 590 585 </div> 591 - 592 - <style> 593 - .oauth-register-container { 594 - max-width: var(--width-lg); 595 - margin: var(--space-9) auto; 596 - padding: var(--space-7); 597 - } 598 - 599 - .loading, .creating { 600 - display: flex; 601 - flex-direction: column; 602 - align-items: center; 603 - gap: var(--space-4); 604 - padding: var(--space-8); 605 - } 606 - 607 - .loading p, .creating p { 608 - color: var(--text-secondary); 609 - } 610 - 611 - .page-header { 612 - margin-bottom: var(--space-6); 613 - } 614 - 615 - .page-header h1 { 616 - margin: 0 0 var(--space-2) 0; 617 - } 618 - 619 - .subtitle { 620 - color: var(--text-secondary); 621 - margin: 0; 622 - } 623 - 624 - .form-section { 625 - min-width: 0; 626 - } 627 - 628 - .form-links { 629 - margin-top: var(--space-6); 630 - } 631 - 632 - .link-text { 633 - text-align: center; 634 - color: var(--text-secondary); 635 - } 636 - 637 - .link-text a { 638 - color: var(--accent); 639 - } 640 - 641 - form { 642 - display: flex; 643 - flex-direction: column; 644 - gap: var(--space-5); 645 - } 646 - 647 - .field { 648 - display: flex; 649 - flex-direction: column; 650 - gap: var(--space-1); 651 - } 652 - 653 - label { 654 - font-size: var(--text-sm); 655 - font-weight: var(--font-medium); 656 - } 657 - 658 - input, select { 659 - padding: var(--space-3); 660 - border: 1px solid var(--border-color); 661 - border-radius: var(--radius-md); 662 - font-size: var(--text-base); 663 - background: var(--bg-input); 664 - color: var(--text-primary); 665 - } 666 - 667 - input:focus, select:focus { 668 - outline: none; 669 - border-color: var(--accent); 670 - } 671 - 672 - .hint { 673 - font-size: var(--text-xs); 674 - color: var(--text-muted); 675 - margin: var(--space-1) 0 0 0; 676 - } 677 - 678 - .error { 679 - padding: var(--space-3); 680 - background: var(--error-bg); 681 - border: 1px solid var(--error-border); 682 - border-radius: var(--radius-md); 683 - color: var(--error-text); 684 - margin-bottom: var(--space-4); 685 - } 686 - 687 - .actions { 688 - display: flex; 689 - gap: var(--space-4); 690 - margin-top: var(--space-2); 691 - } 692 - 693 - button.primary { 694 - flex: 1; 695 - padding: var(--space-3); 696 - background: var(--accent); 697 - color: var(--text-inverse); 698 - border: none; 699 - border-radius: var(--radius-md); 700 - font-size: var(--text-base); 701 - cursor: pointer; 702 - transition: background-color var(--transition-fast); 703 - } 704 - 705 - button.primary:hover:not(:disabled) { 706 - background: var(--accent-hover); 707 - } 708 - 709 - button.primary:disabled { 710 - opacity: 0.6; 711 - cursor: not-allowed; 712 - } 713 - 714 - .secondary-actions { 715 - display: flex; 716 - justify-content: center; 717 - gap: var(--space-4); 718 - margin-top: var(--space-4); 719 - } 720 - 721 - .link-btn { 722 - background: none; 723 - border: none; 724 - color: var(--accent); 725 - cursor: pointer; 726 - font-size: var(--text-sm); 727 - padding: var(--space-2); 728 - } 729 - 730 - .link-btn:hover { 731 - text-decoration: underline; 732 - } 733 - 734 - .contact-fields { 735 - display: flex; 736 - flex-direction: column; 737 - gap: var(--space-4); 738 - } 739 - 740 - .required { 741 - color: var(--error-text); 742 - } 743 - 744 - .passkey-step { 745 - display: flex; 746 - flex-direction: column; 747 - gap: var(--space-4); 748 - } 749 - 750 - .passkey-step h2 { 751 - margin: 0; 752 - } 753 - 754 - .passkey-step p { 755 - color: var(--text-secondary); 756 - margin: 0; 757 - } 758 - 759 - fieldset { 760 - border: 1px solid var(--border-color); 761 - border-radius: var(--radius-md); 762 - padding: var(--space-4); 763 - } 764 - 765 - legend { 766 - padding: 0 var(--space-2); 767 - font-weight: var(--font-medium); 768 - } 769 - 770 - .spinner { 771 - width: 32px; 772 - height: 32px; 773 - border: 3px solid var(--border-color); 774 - border-top-color: var(--accent); 775 - border-radius: 50%; 776 - animation: spin 1s linear infinite; 777 - } 778 - 779 - @keyframes spin { 780 - to { 781 - transform: rotate(360deg); 782 - } 783 - } 784 - </style>

History

1 round 0 comments
sign up or login to add to the discussion
oyster.cafe submitted #0
1 commit
expand
refactor(frontend): extract oauth primary flow page styles
expand 0 comments
pull request successfully merged