diff --git a/packages/docs/guides/style-guide.md b/packages/docs/guides/style-guide.md index 57ae39285..9b811e7b5 100644 --- a/packages/docs/guides/style-guide.md +++ b/packages/docs/guides/style-guide.md @@ -9,47 +9,98 @@ ### Logo/Brand Icon - Circular checkmark icon -- Primary color: `blue-600` +- Primary color: `primary` - White checkmark symbol inside -## Color Palette +## Design Tokens + +The application uses a **shadcn-style semantic token system** for theming. Tokens are defined in `global.css` and support light/dark modes. + +### Token Reference + +| Token | Light Value | Usage | +| ------------------------ | ----------- | ------------------------- | +| `background` | slate-50 | Page backgrounds | +| `foreground` | slate-900 | Primary text | +| `card` | white | Card surfaces | +| `card-foreground` | slate-900 | Card text | +| `popover` | white | Dropdown/popover surfaces | +| `popover-foreground` | slate-900 | Popover text | +| `primary` | blue-600 | Primary actions, links | +| `primary-foreground` | white | Text on primary | +| `secondary` | slate-100 | Secondary buttons | +| `secondary-foreground` | slate-700 | Secondary button text | +| `muted` | slate-100 | Subtle backgrounds | +| `muted-foreground` | slate-500 | Muted/secondary text | +| `accent` | slate-100 | Hover highlights | +| `accent-foreground` | slate-900 | Accent text | +| `destructive` | red-600 | Danger/delete actions | +| `destructive-foreground` | white | Text on destructive | +| `success` | emerald-600 | Success states | +| `success-foreground` | white | Text on success | +| `warning` | amber-500 | Warning states | +| `warning-foreground` | white | Text on warning | +| `border` | slate-200 | Default borders | +| `border-subtle` | slate-100 | Subtle dividers | +| `input` | slate-200 | Input borders | +| `ring` | blue-600 | Focus rings | + +### Using Tokens -### Primary Colors +```jsx +// Backgrounds +
// Page background +
// Card surface +
// Subtle background +
// Primary action background + +// Text +

// Primary text +

// Secondary/muted text +

// Link/accent text + +// Borders +

// Default border +
// Subtle divider + +// Buttons + ``` @@ -118,7 +169,7 @@ Section labels use uppercase tracking: #### Secondary Button ```jsx - ``` @@ -126,13 +177,21 @@ Section labels use uppercase tracking: #### Text Button (Links) ```jsx - + +``` + +#### Destructive Button + +```jsx + ``` #### Icon Button ```jsx - ``` @@ -140,7 +199,7 @@ Section labels use uppercase tracking: #### Disabled State (Quota Limit) ```jsx - + Invite @@ -151,7 +210,7 @@ Section labels use uppercase tracking: #### Input Field ```jsx - + ``` #### Select (Ark UI) @@ -161,7 +220,13 @@ Uses the `@/components/ui/select` component with `createListCollection`. #### Label ```jsx - + +``` + +#### Muted Label (Uppercase) + +```jsx + ``` ### Cards & Containers @@ -169,8 +234,8 @@ Uses the `@/components/ui/select` component with `createListCollection`. #### Standard Card (Project View) ```jsx -
-

Section Title

+
+

Section Title

{/* Content */}
``` @@ -178,16 +243,16 @@ Uses the `@/components/ui/select` component with `createListCollection`. #### Settings Card (with Header) ```jsx -
+
{/* Header */} -
+
-
- +
+
-

Section Title

-

Description text

+

Section Title

+

Description text

@@ -199,12 +264,12 @@ Uses the `@/components/ui/select` component with `createListCollection`. #### Stat Card ```jsx -
+
- +
-

42

-

Label

+

42

+

Label

``` @@ -212,26 +277,26 @@ Uses the `@/components/ui/select` component with `createListCollection`. ```jsx // Success variant -
-

12

-

Ready

+
+

12

+

Ready

-// Info variant -
-

8

-

Completed

+// Primary/Info variant +
+

8

+

Completed

``` #### Collapsible Section ```jsx -
+
- -

Section Title

-
+ +

Section Title

+
Click to expand @@ -239,7 +304,7 @@ Uses the `@/components/ui/select` component with `createListCollection`.
-
{/* Content */}
+
{/* Content */}
@@ -250,7 +315,7 @@ Uses the `@/components/ui/select` component with `createListCollection`. #### Page Header (Sticky) ```jsx -
+
{/* Header content */}
``` @@ -262,18 +327,18 @@ Uses `@/components/ui/tabs` components: ```jsx {icon} Tab Label - + ``` #### Tab Badge ```jsx - + {count} ``` @@ -283,7 +348,7 @@ Uses `@/components/ui/tabs` components: #### Role Badge ```jsx - + owner ``` @@ -291,7 +356,7 @@ Uses `@/components/ui/tabs` components: #### Status Badge (Verified) ```jsx - + Verified @@ -300,7 +365,7 @@ Uses `@/components/ui/tabs` components: #### Count Badge (Project Card) ```jsx - + {role} ``` @@ -312,7 +377,7 @@ Uses `@/components/ui/avatar` component: ```jsx - {getInitials(name)} + {getInitials(name)} ``` @@ -320,9 +385,9 @@ Uses `@/components/ui/avatar` component: ```jsx
- -
``` @@ -330,7 +395,7 @@ Uses `@/components/ui/avatar` component: #### Avatar Fallback (Initials) ```jsx -
+
{initials}
``` @@ -340,10 +405,7 @@ Uses `@/components/ui/avatar` component: #### Alternating Row ```jsx -
+
{/* Row content */}
``` @@ -351,7 +413,7 @@ Uses `@/components/ui/avatar` component: #### Divider Between Sections ```jsx -
+
``` ### Modals & Overlays @@ -403,13 +465,13 @@ See `ChartSection.jsx` for chart implementations. ### Hover Effects - **Shadow**: `hover:shadow-md` for cards -- **Background**: `hover:bg-slate-50` for interactive rows -- **Text Color**: `hover:text-blue-700` for links +- **Background**: `hover:bg-muted` for interactive rows +- **Text Color**: `hover:text-primary/80` for links ### Focus States -- **Ring**: `focus:ring-2 focus:ring-blue-500/20` -- **Border**: `focus:border-blue-500` +- **Ring**: `focus:ring-2 focus:ring-ring/20` +- **Border**: `focus:border-ring` - **Outline**: `focus:outline-none` ## Page Layouts @@ -417,12 +479,12 @@ See `ChartSection.jsx` for chart implementations. ### Settings Page ```jsx -
+
{/* Page header */}
-

Page Title

-

Page description

+

Page Title

+

Page description

{/* Cards */} @@ -434,9 +496,9 @@ See `ChartSection.jsx` for chart implementations. ### Project View ```jsx -
+
{/* Sticky header with tabs */} -
+
{/* Header + Tabs */}
@@ -452,14 +514,14 @@ See `ChartSection.jsx` for chart implementations. All interactive elements use: ``` -focus:ring-2 focus:ring-blue-500/20 focus:outline-none +focus:ring-2 focus:ring-ring/20 focus:outline-none ``` ### Color Contrast -- Primary text (`slate-900`) on white backgrounds: 15.4:1 -- Secondary text (`slate-500`) on white backgrounds: 5.6:1 -- Blue links (`blue-600`) on white backgrounds: 4.5:1 +- `foreground` on `card` backgrounds: 15.4:1 +- `muted-foreground` on `card` backgrounds: 5.6:1 +- `primary` on `card` backgrounds: 4.5:1 ### Interactive Elements diff --git a/packages/web/src/components/auth/AuthButtons.jsx b/packages/web/src/components/auth/AuthButtons.jsx index 6b950739d..82def9475 100644 --- a/packages/web/src/components/auth/AuthButtons.jsx +++ b/packages/web/src/components/auth/AuthButtons.jsx @@ -1,5 +1,4 @@ import { AnimatedShow } from '../AnimatedShow.jsx'; -import { AiOutlineLoading3Quarters } from 'solid-icons/ai'; /** * Primary button for auth forms @@ -18,11 +17,16 @@ export function PrimaryButton(props) { class='flex w-full items-center justify-center rounded-lg bg-blue-600 py-2 text-sm font-bold text-white shadow transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-50 sm:rounded-xl sm:py-3 sm:text-base' disabled={props.disabled || props.loading} onClick={() => props.onClick?.()} + aria-busy={props.loading} >
- - {props.loadingText || 'Loading...'} +
+ {props.loadingText || 'Loading...'}
diff --git a/packages/web/src/components/auth/AuthLayout.jsx b/packages/web/src/components/auth/AuthLayout.jsx index 1c630ea4e..5d177d624 100644 --- a/packages/web/src/components/auth/AuthLayout.jsx +++ b/packages/web/src/components/auth/AuthLayout.jsx @@ -60,9 +60,7 @@ export default function AuthLayout(props) {
- -
{props.children}
-
+ {props.children}
); } diff --git a/packages/web/src/components/auth/CheckEmail.jsx b/packages/web/src/components/auth/CheckEmail.jsx index 3588ac873..91048549d 100644 --- a/packages/web/src/components/auth/CheckEmail.jsx +++ b/packages/web/src/components/auth/CheckEmail.jsx @@ -121,68 +121,71 @@ export default function CheckEmail() { }; return ( -
-
- {/* Logo */} - - CoRATES - - - {loading() ? - <> -
- -
-

Email Verified!

-

Redirecting you to the dashboard...

- - : <> -
-
- -
-
- -
-

Check Your Email

-

We've sent a verification email to:

-

{email()}

+
+ {/* Logo */} + + CoRATES + + + {loading() ? + <> +
+ +
+

Email Verified!

+

Redirecting you to the dashboard...

+ + : <> +
+
+
+
-
-

- Click the verification link in your email to activate your account. Once verified, - you'll automatically be redirected to the dashboard. -

+
+

Check Your Email

+

We've sent a verification email to:

+

{email()}

+
- +
+

+ Click the verification link in your email to activate your account. Once verified, + you'll automatically be redirected to the dashboard. +

- {resent() && ( -
- Verification email sent successfully! -
- )} -
+ -
- - Resend Email - - - Back to Sign In -
- -
-

Didn't receive the email? Check your spam folder or try resending.

-
- - } -
+ {resent() && ( +
+ Verification email sent successfully! +
+ )} +
+ +
+ + Resend Email + + + Back to Sign In +
+ +
+

Didn't receive the email? Check your spam folder or try resending.

+
+ + }
); } diff --git a/packages/web/src/components/auth/CompleteProfile.jsx b/packages/web/src/components/auth/CompleteProfile.jsx index ba1d5da11..5dcda09bd 100644 --- a/packages/web/src/components/auth/CompleteProfile.jsx +++ b/packages/web/src/components/auth/CompleteProfile.jsx @@ -266,287 +266,281 @@ export default function CompleteProfile() { const displayError = () => error(); return ( -
- - } - > -
- {/* Logo */} - - CoRATES - - - - {/* Step Indicator */} - - - {(stepInfo, index) => ( - - currentStep() + 1} - > - - - - - - - - - - - - )} - - - - {/* Step 1: Name and Title */} - -
-

- Complete Your Profile -

-

- Just a few details to get you started -

-
- -
- {/* Name fields - side by side */} -
-
- - { - setHasEditedName(true); - setFirstName(e.target.value); - }} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none' - required - id='first-name-input' - placeholder='First' - /> -
-
- - { - setHasEditedName(true); - setLastName(e.target.value); - }} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none' - required - id='last-name-input' - placeholder='Last' - /> -
-
- - {/* Title dropdown */} -
- - - - setCustomTitle(e.target.value)} - class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none' - placeholder='Enter your title' - maxLength={50} - /> + + + + + + + + + -
- - - - Next - -
- - {/* Step 2: Institution Details */} - -
-

- Institution Details -

-

- Optional - helps us understand your background -

-
- -
- {/* Institution */} + + )} + + + + {/* Step 1: Name and Title */} + +
+

+ Complete Your Profile +

+

Just a few details to get you started

+
+ + + {/* Name fields - side by side */} +
setInstitution(e.target.value)} + autoComplete='given-name' + autocapitalize='words' + spellcheck='false' + value={firstName()} + onInput={e => { + setHasEditedName(true); + setFirstName(e.target.value); + }} class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none' - placeholder='e.g., University of Oxford' - maxLength={200} + required + id='first-name-input' + placeholder='First' + aria-describedby={displayError() ? 'profile-step1-error' : undefined} />
- - {/* Department */}
setDepartment(e.target.value)} + autoComplete='family-name' + autocapitalize='words' + spellcheck='false' + value={lastName()} + onInput={e => { + setHasEditedName(true); + setLastName(e.target.value); + }} class='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none' - placeholder='e.g., Department of Medicine' - maxLength={200} + required + id='last-name-input' + placeholder='Last' + aria-describedby={displayError() ? 'profile-step1-error' : undefined} />
+
- - -
- - Back - - -
- - - -
- - {/* Completed Content (shows after all steps) */} - -
-
- -
-

All Done!

-

Redirecting to your dashboard...

-
-
-
-
-
+ + + + + + {/* Step 3: Persona Selection */} + +
+

+ What best describes you? +

+

This helps us tailor your experience

+
+ +
+ + + + +
+ + Back + + + Finish Setup + +
+ + + +
+ + {/* Completed Content (shows after all steps) */} + +
+
+ +
+

All Done!

+

Redirecting to your dashboard...

+
+
+ +
+ ); } diff --git a/packages/web/src/components/auth/ErrorMessage.jsx b/packages/web/src/components/auth/ErrorMessage.jsx index 639d4d0f0..3e4033470 100644 --- a/packages/web/src/components/auth/ErrorMessage.jsx +++ b/packages/web/src/components/auth/ErrorMessage.jsx @@ -1,9 +1,19 @@ import { AnimatedShow } from '../AnimatedShow.jsx'; +/** + * Error message component with proper accessibility attributes + * AnimatedShow already provides role="alert" and aria-live by default + * @param {Object} props + * @param {Function} props.displayError - Signal returning error string or null + * @param {string} props.id - Optional ID for aria-describedby linking + */ export default function ErrorMessage(props) { return ( - -
+ +
{props.displayError()}
diff --git a/packages/web/src/components/auth/MagicLinkForm.jsx b/packages/web/src/components/auth/MagicLinkForm.jsx index 78916868c..ea086fb5b 100644 --- a/packages/web/src/components/auth/MagicLinkForm.jsx +++ b/packages/web/src/components/auth/MagicLinkForm.jsx @@ -100,7 +100,7 @@ export default function MagicLinkForm(props) { Click the link in the email to sign in. The link expires in 10 minutes.

- +
@@ -159,10 +159,11 @@ export default function MagicLinkForm(props) { id='magic-link-email' placeholder='you@example.com' disabled={loading()} + aria-describedby={displayError() ? 'magic-link-error' : undefined} />
- + {props.buttonText || 'Send Sign-In Link'} diff --git a/packages/web/src/components/auth/ProtectedGuard.jsx b/packages/web/src/components/auth/ProtectedGuard.jsx index 385ac58a7..d8f8f74bf 100644 --- a/packages/web/src/components/auth/ProtectedGuard.jsx +++ b/packages/web/src/components/auth/ProtectedGuard.jsx @@ -5,16 +5,16 @@ import { PageLoader } from '@/components/ui/spinner'; /** * ProtectedGuard - For authenticated pages (profile, settings, admin, etc.) - * Redirects guests to dashboard + * Redirects guests to signin */ export default function ProtectedGuard(props) { const { isLoggedIn, authLoading } = useBetterAuth(); const navigate = useNavigate(); - // Redirect non-logged-in users to dashboard + // Redirect non-logged-in users to signin createEffect(() => { if (!authLoading() && !isLoggedIn()) { - navigate('/dashboard', { replace: true }); + navigate('/signin', { replace: true }); } }); diff --git a/packages/web/src/components/auth/ResetPassword.jsx b/packages/web/src/components/auth/ResetPassword.jsx index 9325c2f42..821e13a60 100644 --- a/packages/web/src/components/auth/ResetPassword.jsx +++ b/packages/web/src/components/auth/ResetPassword.jsx @@ -5,6 +5,12 @@ import { AnimatedShow } from '../AnimatedShow.jsx'; import ErrorMessage from './ErrorMessage.jsx'; import { PrimaryButton, AuthLink } from './AuthButtons.jsx'; import StrengthIndicator from './StrengthIndicator.jsx'; +import { + PasswordInput, + PasswordInputControl, + PasswordInputField, + PasswordInputVisibilityTrigger, +} from '@/components/ui/password-input'; import { handleError } from '@/lib/error-utils.js'; const REDIRECT_DELAY_MS = 3000; @@ -14,17 +20,15 @@ export default function ResetPassword() { const token = () => searchParams.token; return ( -
-
- {/* Logo */} - - CoRATES - - - }> - - -
+
+ {/* Logo */} + + CoRATES + + + }> + +
); } @@ -203,21 +207,22 @@ function SetNewPasswordForm(props) {
- setPassword(e.target.value)} - class='w-full rounded-lg border border-gray-300 py-2 pr-3 pl-3 text-xs transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none sm:pr-4 sm:pl-4 sm:text-sm' - required - id='password-input' - placeholder='Enter new password' - disabled={loading()} - /> + + + setPassword(e.target.value)} + placeholder='Enter new password' + aria-describedby={displayError() ? 'reset-password-error' : undefined} + /> + + +
@@ -228,20 +233,21 @@ function SetNewPasswordForm(props) { > Confirm Password - setConfirmPassword(e.target.value)} - class='w-full rounded-lg border border-gray-300 py-2 pr-3 pl-3 text-xs transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none sm:pr-4 sm:pl-4 sm:text-sm' - required - id='confirm-password-input' - placeholder='Confirm new password' - disabled={loading()} - /> + + + setConfirmPassword(e.target.value)} + placeholder='Confirm new password' + aria-describedby={displayError() ? 'reset-password-error' : undefined} + /> + + +
- + Set Password diff --git a/packages/web/src/components/auth/RoleSelector.jsx b/packages/web/src/components/auth/RoleSelector.jsx index 1de134d92..eb331be4e 100644 --- a/packages/web/src/components/auth/RoleSelector.jsx +++ b/packages/web/src/components/auth/RoleSelector.jsx @@ -46,25 +46,31 @@ export function getRoleLabel(roleId) { /** * Role selection grid for sign up flow + * Uses radiogroup pattern for single-select accessibility * @param {Object} props * @param {string} props.selectedRole - Currently selected role ID * @param {Function} props.onSelect - Callback when role is selected */ export default function RoleSelector(props) { return ( -
+
{roleOption => ( diff --git a/packages/web/src/components/auth/SignIn.jsx b/packages/web/src/components/auth/SignIn.jsx index 20582475b..6f73686cd 100644 --- a/packages/web/src/components/auth/SignIn.jsx +++ b/packages/web/src/components/auth/SignIn.jsx @@ -1,4 +1,4 @@ -import { createSignal, onCleanup, onMount, Show } from 'solid-js'; +import { createEffect, createSignal, onCleanup, onMount, Show } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import { useBetterAuth } from '@api/better-auth-store.js'; import { @@ -29,6 +29,9 @@ export default function SignIn() { const [orcidLoading, setOrcidLoading] = createSignal(false); const [useMagicLink, setUseMagicLink] = createSignal(false); const [showTwoFactor, setShowTwoFactor] = createSignal(false); + const [formHeight, setFormHeight] = createSignal('auto'); + let passwordFormRef; + let magicLinkFormRef; const navigate = useNavigate(); const { signin, signinWithGoogle, signinWithOrcid, authError, clearAuthError } = useBetterAuth(); @@ -63,6 +66,28 @@ export default function SignIn() { }); }); + // Update form height based on which form is active + const updateFormHeight = () => { + const activeRef = useMagicLink() ? magicLinkFormRef : passwordFormRef; + if (activeRef) { + setFormHeight(`${activeRef.offsetHeight}px`); + } + }; + + // Set initial height after mount when refs are ready + onMount(() => { + // Small delay to ensure refs are measured after initial render + requestAnimationFrame(updateFormHeight); + }); + + // Update height when switching forms + createEffect(() => { + // Track the signal + useMagicLink(); + // Update after a frame to ensure layout is complete + requestAnimationFrame(updateFormHeight); + }); + // Watch for auth errors from the store const displayError = () => error() || authError(); @@ -149,152 +174,216 @@ export default function SignIn() { } return ( -
-
- {/* Logo */} - - CoRATES - - - {/* Two-Factor Verification */} - - - - - {/* Normal Sign In */} - -
-

+ {/* Logo */} + + CoRATES + + + {/* Two-Factor Verification */} + + + + + {/* Normal Sign In */} + +
+

+ Welcome Back +

+

Sign in to your account.

+
+ + + + {/* Toggle between password and magic link */} +
{ + if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { + e.preventDefault(); + const goToMagicLink = e.key === 'ArrowRight'; + setUseMagicLink(goToMagicLink); + document.getElementById(goToMagicLink ? 'tab-magic-link' : 'tab-password')?.focus(); + } + }} + > + {/* Sliding indicator */} + + + {/* Sliding form container */} +
+
+ {/* Password Form */} +
- Welcome Back -

-

Sign in to your account.

-
- - - - {/* Toggle between password and magic link */} -
- - -
- - {/* Magic Link Form */} - - - - - {/* Password Form */} - -
-
-
- - setEmail(e.target.value)} - class='w-full rounded-lg border border-gray-300 py-2 pr-3 pl-3 text-xs transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none sm:pr-4 sm:pl-4 sm:text-sm' - required - id='email-input' - placeholder='you@example.com' - disabled={loading()} - /> -
- -
- - - setPassword(e.target.value)} - placeholder='Password' - /> - - - -
- - - - - Sign In - - -
- { - e.preventDefault(); - navigate('/reset-password'); - }} - > - Forgot password? - + +
+
+ + setEmail(e.target.value)} + class='w-full rounded-lg border border-gray-300 py-2 pr-3 pl-3 text-xs transition focus:border-transparent focus:ring-2 focus:ring-blue-500 focus:outline-none sm:pr-4 sm:pl-4 sm:text-sm' + required + id='email-input' + placeholder='you@example.com' + disabled={loading()} + aria-describedby={displayError() ? 'signin-error' : undefined} + /> +
+ +
+ + + + setPassword(e.target.value)} + placeholder='Password' + aria-describedby={displayError() ? 'signin-error' : undefined} + /> + + + +
+ + + + + Sign In + + +
+ { + e.preventDefault(); + navigate('/reset-password'); + }} + > + Forgot password? + +
-
- - - - - - - 1} - /> - 1} - /> - - -
- Don't have an account?{' '} - { - e.preventDefault(); - navigate('/signup'); - }} + +
+ + {/* Magic Link Form */} +
-
-
+
+ + + + + 1} + /> + 1} + /> + + +
+ Don't have an account?{' '} + { + e.preventDefault(); + navigate('/signup'); + }} + > + Sign Up + +
+
); } diff --git a/packages/web/src/components/auth/SignUp.jsx b/packages/web/src/components/auth/SignUp.jsx index 851dd5c94..ef63d422d 100644 --- a/packages/web/src/components/auth/SignUp.jsx +++ b/packages/web/src/components/auth/SignUp.jsx @@ -108,79 +108,75 @@ export default function SignUp() { } return ( -
-
- {/* Logo */} - - CoRATES - +
+ {/* Logo */} + + CoRATES + + +
+

Create an Account

+

Get started with CoRATES

+
-
-

- Create an Account -

-

Get started with CoRATES

-
- - {/* Social providers */} - - 1} - /> - 1} - /> - - - - - - - {/* Magic Link Form - simple email signup */} - + 1} /> - -

- By continuing, you agree to our{' '} - - Terms of Service - {' '} - and{' '} - - Privacy Policy - - . -

- -
- Already have an account?{' '} - { - e.preventDefault(); - navigate('/signin'); - }} - > - Sign In - -
+ 1} + /> + + + + + + + {/* Magic Link Form - simple email signup */} + + +

+ By continuing, you agree to our{' '} + + Terms of Service + {' '} + and{' '} + + Privacy Policy + + . +

+ +
+ Already have an account?{' '} + { + e.preventDefault(); + navigate('/signin'); + }} + > + Sign In +
); diff --git a/packages/web/src/components/auth/SocialAuthButtons.jsx b/packages/web/src/components/auth/SocialAuthButtons.jsx index 78c3b4d33..ee61652f4 100644 --- a/packages/web/src/components/auth/SocialAuthButtons.jsx +++ b/packages/web/src/components/auth/SocialAuthButtons.jsx @@ -23,9 +23,13 @@ export function GoogleButton(props) { > } + fallback={} > -
+
Continue with Google @@ -37,12 +41,19 @@ export function GoogleButton(props) { disabled={props.loading} class={`${baseClass} p-3 sm:p-3.5`} title='Continue with Google' + aria-label='Continue with Google' > } + fallback={ + + } > -
+
@@ -72,9 +83,13 @@ export function OrcidButton(props) { > } + fallback={} > -
+
Continue with ORCID @@ -86,12 +101,19 @@ export function OrcidButton(props) { disabled={props.loading} class={`${baseClass} p-3 sm:p-3.5`} title='Continue with ORCID' + aria-label='Continue with ORCID' > } + fallback={ + + } > -
+
diff --git a/packages/web/src/components/auth/TwoFactorVerify.jsx b/packages/web/src/components/auth/TwoFactorVerify.jsx index 3cded284a..a61c67e48 100644 --- a/packages/web/src/components/auth/TwoFactorVerify.jsx +++ b/packages/web/src/components/auth/TwoFactorVerify.jsx @@ -92,10 +92,11 @@ export default function TwoFactorVerify(props) { disabled={loading()} id='2fa-code' autoFocus + aria-describedby={displayError() ? '2fa-error' : undefined} />
- + Verify diff --git a/packages/web/src/components/settings/pages/AcademicInfoSection.jsx b/packages/web/src/components/settings/pages/AcademicInfoSection.jsx index 861fb6fad..5978684c4 100644 --- a/packages/web/src/components/settings/pages/AcademicInfoSection.jsx +++ b/packages/web/src/components/settings/pages/AcademicInfoSection.jsx @@ -100,7 +100,7 @@ export default function AcademicInfoSection() { }; return ( -
+