style: auto-format codebase with Prettier

Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
This commit is contained in:
Wuesteon 2025-11-27 18:33:16 +01:00
parent 0241f5554c
commit d36b321d9d
3952 changed files with 661498 additions and 739751 deletions

View file

@ -34,7 +34,7 @@
resendEmail: 'Resend Email',
successMessage: "We've sent a password reset link to {email}. Please check your inbox.",
emailRequired: 'Email is required',
sendFailed: 'Failed to send reset email'
sendFailed: 'Failed to send reset email',
};
interface Props {
@ -70,7 +70,7 @@
lightBackground = '#f5f5f5',
darkBackground = '#121212',
appSlider,
translations = {}
translations = {},
}: Props = $props();
// Merge provided translations with defaults
@ -142,7 +142,9 @@
<div class="flex flex-col items-center justify-center pt-16 pb-8">
<div
class="flex items-center justify-center rounded-full transition-all mb-4"
style="width: 120px; height: 120px; border: 3px solid {primaryColor}; background-color: {isDark ? '#000' : '#fff'}; box-shadow: {isDark
style="width: 120px; height: 120px; border: 3px solid {primaryColor}; background-color: {isDark
? '#000'
: '#fff'}; box-shadow: {isDark
? '0 6px 12px rgba(0, 0, 0, 0.4)'
: '0 6px 12px rgba(0, 0, 0, 0.15)'};"
>
@ -157,7 +159,11 @@
<div class="flex-1 flex items-start justify-center px-5 pt-8 pb-8">
<div
class="w-full max-w-md rounded-xl p-6"
style="background-color: {isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"
style="background-color: {isDark
? 'rgba(255, 255, 255, 0.08)'
: 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(0, 0, 0, 0.1)'};"
>
<!-- Title -->
<h2
@ -197,7 +203,13 @@
placeholder={t.emailPlaceholder}
required
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {primaryColor};"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {primaryColor};"
/>
</div>
@ -205,7 +217,9 @@
type="submit"
disabled={loading}
class="flex h-14 w-full items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 disabled:opacity-50 border-2"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark ? '#ffffff' : '#000000'};"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark
? '#ffffff'
: '#000000'};"
>
<Icon name="key" size={20} />
{loading ? t.sending : t.sendResetLinkButton}
@ -224,7 +238,7 @@
</button>
</div>
<!-- Success Mode -->
<!-- Success Mode -->
{:else}
<div class="pb-4">
<div class="flex flex-col items-center mb-6">
@ -239,7 +253,10 @@
class="text-sm text-center px-2"
style="color: {isDark ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.7)'};"
>
{@html getSuccessMessage(resetEmail).replace(resetEmail, `<strong>${resetEmail}</strong>`)}
{@html getSuccessMessage(resetEmail).replace(
resetEmail,
`<strong>${resetEmail}</strong>`
)}
</p>
</div>
@ -247,7 +264,9 @@
<button
onclick={() => goto(loginPath)}
class="flex h-14 items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 border-2"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark ? '#ffffff' : '#000000'};"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark
? '#ffffff'
: '#000000'};"
>
<Icon name="sign-in" size={20} />
{t.backToLogin}
@ -259,7 +278,11 @@
error = null;
}}
class="flex h-10 items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 border"
style="background-color: {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'};"
style="background-color: {isDark
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'};"
>
{t.resendEmail}
</button>

View file

@ -56,7 +56,7 @@
signInFailed: 'Sign in failed',
googleSignInFailed: 'Google sign in failed',
signInSuccess: 'Successfully signed in. Redirecting...',
googleSignInSuccess: 'Successfully signed in with Google. Redirecting...'
googleSignInSuccess: 'Successfully signed in with Google. Redirecting...',
};
interface Props {
@ -113,7 +113,7 @@
darkBackground = '#121212',
appSlider,
headerControls,
translations = {}
translations = {},
}: Props = $props();
// Merge provided translations with defaults
@ -259,11 +259,311 @@
<title>Login - {appName}</title>
</svelte:head>
<!-- Skip Link for keyboard users -->
<button class="skip-link" onclick={skipToForm} type="button">
{t.skipToForm}
</button>
<!-- Screen reader announcements -->
<div aria-live="polite" aria-atomic="true" class="sr-only">
{successAnnouncement}
</div>
<div
class="flex min-h-screen flex-col justify-between"
style="background-color: {getPageBackground()};"
>
<!-- Header Controls (Theme Toggle, Language Selector, etc.) -->
{#if headerControls}
<div class="absolute right-4 top-4 z-50 flex items-center gap-3 opacity-60">
{@render headerControls()}
</div>
{/if}
<main>
<!-- Top Section - Logo -->
<div class="flex flex-col items-center justify-center pt-16 pb-8">
<div
class="flex items-center justify-center rounded-full transition-all mb-4"
class:success-pulse={showSuccess}
style="width: 120px; height: 120px; border: 3px solid {showSuccess
? '#22c55e'
: primaryColor}; background-color: {isDark ? '#000' : '#fff'}; box-shadow: {isDark
? '0 6px 12px rgba(0, 0, 0, 0.4)'
: '0 6px 12px rgba(0, 0, 0, 0.15)'};"
role="img"
aria-label="{appName} logo"
>
{#if showSuccess}
<Icon name="check" size={55} color="#22c55e" />
{:else}
<Logo size={55} color={primaryColor} />
{/if}
</div>
<h1 class="text-2xl font-semibold" style="color: {isDark ? '#ffffff' : '#000000'};">
{appName}
</h1>
</div>
<!-- Middle Section - Auth Form -->
<div class="flex-1 flex items-start justify-center px-5 pt-8 pb-8">
<div
class="w-full max-w-md rounded-xl p-6"
class:shake={shakeError}
style="background-color: {isDark
? 'rgba(255, 255, 255, 0.08)'
: 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(0, 0, 0, 0.1)'};"
>
<!-- Title -->
<div class="mb-6">
<h2
class="text-center text-xl font-semibold"
style="color: {isDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'};"
>
{t.title}
</h2>
<p
class="mt-2 text-sm text-center"
style="color: {isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};"
>
{t.subtitle}
</p>
</div>
<!-- Error Messages -->
{#if error}
<div
id="form-error"
role="alert"
aria-live="assertive"
class="mb-4 rounded-xl bg-red-500/20 border border-red-500/30 p-3 flex items-center gap-2"
>
<Icon name="warning" size={18} color="#ef4444" />
<p class="text-sm text-red-500">{error}</p>
</div>
{/if}
<!-- Login Form -->
<form
onsubmit={(e) => {
e.preventDefault();
handleLogin();
}}
class="pb-4"
aria-busy={loading}
aria-describedby={error ? 'form-error' : undefined}
>
<!-- Email Field -->
<div class="mb-3">
<label for="email" class="sr-only">{t.emailPlaceholder}</label>
<input
id="email"
type="email"
bind:this={emailInput}
bind:value={email}
placeholder={t.emailPlaceholder}
required
autocomplete="email"
aria-invalid={errorField === 'email'}
aria-describedby={errorField === 'email' ? 'form-error' : undefined}
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {errorField === 'email'
? '#ef4444'
: isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {errorField === 'email' ? '#ef4444' : primaryColor};"
/>
</div>
<!-- Password Field -->
<div class="mb-3 relative">
<label for="password" class="sr-only">{t.passwordPlaceholder}</label>
<input
id="password"
type={showPassword ? 'text' : 'password'}
bind:this={passwordInput}
bind:value={password}
placeholder={t.passwordPlaceholder}
required
autocomplete="current-password"
aria-invalid={errorField === 'password'}
aria-describedby={errorField === 'password' ? 'form-error' : undefined}
class="h-14 w-full rounded-xl border px-4 pr-12 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {errorField === 'password'
? '#ef4444'
: isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {errorField === 'password'
? '#ef4444'
: primaryColor};"
/>
<button
type="button"
onclick={() => (showPassword = !showPassword)}
class="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg hover:bg-black/10 dark:hover:bg-white/10 transition-colors touch-target flex items-center justify-center"
aria-label={showPassword ? t.hidePassword : t.showPassword}
aria-pressed={showPassword}
title={showPassword ? t.hidePassword : t.showPassword}
>
<Icon
name={showPassword ? 'eye-off' : 'eye'}
size={20}
color={isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'}
/>
</button>
</div>
<!-- Remember Me & Forgot Password Row -->
<div class="mb-4 flex items-center justify-between">
<label class="flex items-center gap-2 cursor-pointer touch-target">
<input
type="checkbox"
bind:checked={rememberMe}
class="w-5 h-5 rounded border-2 transition-colors cursor-pointer"
style="accent-color: {primaryColor};"
/>
<span
class="text-sm"
style="color: {isDark ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.7)'};"
>
{t.rememberMe}
</span>
</label>
<button
type="button"
onclick={() => goto(forgotPasswordPath)}
class="text-sm font-medium transition-opacity hover:opacity-70 touch-target flex items-center justify-center px-2"
style="color: {primaryColor};"
>
{t.forgotPassword}
</button>
</div>
<!-- Submit Button -->
<button
type="submit"
disabled={loading || showSuccess}
aria-disabled={loading || showSuccess}
class="flex h-14 w-full items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 disabled:opacity-50 border-2 touch-target"
style="background-color: {showSuccess
? '#22c55e'
: primaryColor + '60'}; border-color: {showSuccess
? '#22c55e'
: primaryColor}; color: {isDark ? '#ffffff' : '#000000'};"
>
{#if loading}
<svg
class="spinner w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10" stroke-opacity="0.25" />
<path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round" />
</svg>
<span>{t.signingIn}</span>
{:else if showSuccess}
<Icon name="check" size={20} />
<span>{t.success}</span>
{:else}
<Icon name="sign-in" size={20} />
<span>{t.signInButton}</span>
{/if}
</button>
</form>
<!-- Social Login -->
{#if enableGoogle || enableApple}
<div class="my-4 flex items-center gap-3" role="separator" aria-orientation="horizontal">
<div
class="flex-1 border-t"
style="border-color: {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"
></div>
<span
class="text-xs"
style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};"
>{t.orDivider}</span
>
<div
class="flex-1 border-t"
style="border-color: {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"
></div>
</div>
<div class="mb-4 flex flex-col gap-2" role="group" aria-label="Social login options">
{#if enableGoogle && onSignInWithGoogle}
<GoogleSignInButton onSuccess={handleGoogleSuccess} />
{/if}
{#if enableApple}
<AppleSignInButton />
{/if}
</div>
{/if}
<!-- Register Link -->
<div class="mt-4 text-center">
<p
class="text-sm"
style="color: {isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};"
>
{t.noAccount}
<button
type="button"
onclick={() => goto(registerPath)}
class="font-medium transition-opacity hover:opacity-70 touch-target inline-flex items-center justify-center px-1"
style="color: {primaryColor};"
>
{t.createAccount}
</button>
</p>
</div>
</div>
</div>
</main>
<!-- App Slider -->
{#if appSlider}
<div class="w-full pb-8 px-2 pt-4">
{@render appSlider()}
</div>
{:else}
<!-- Bottom padding -->
<div class="pb-8"></div>
{/if}
</div>
<style>
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px); }
20%, 40%, 60%, 80% { transform: translateX(4px); }
0%,
100% {
transform: translateX(0);
}
10%,
30%,
50%,
70%,
90% {
transform: translateX(-4px);
}
20%,
40%,
60%,
80% {
transform: translateX(4px);
}
}
.shake {
@ -271,8 +571,12 @@
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.spinner {
@ -280,9 +584,18 @@
}
@keyframes success-pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.05); opacity: 0.9; }
100% { transform: scale(1); opacity: 1; }
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.05);
opacity: 0.9;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.success-pulse {
@ -338,255 +651,3 @@
min-height: 44px;
}
</style>
<!-- Skip Link for keyboard users -->
<button
class="skip-link"
onclick={skipToForm}
type="button"
>
{t.skipToForm}
</button>
<!-- Screen reader announcements -->
<div aria-live="polite" aria-atomic="true" class="sr-only">
{successAnnouncement}
</div>
<div
class="flex min-h-screen flex-col justify-between"
style="background-color: {getPageBackground()};"
>
<!-- Header Controls (Theme Toggle, Language Selector, etc.) -->
{#if headerControls}
<div class="absolute right-4 top-4 z-50 flex items-center gap-3 opacity-60">
{@render headerControls()}
</div>
{/if}
<main>
<!-- Top Section - Logo -->
<div class="flex flex-col items-center justify-center pt-16 pb-8">
<div
class="flex items-center justify-center rounded-full transition-all mb-4"
class:success-pulse={showSuccess}
style="width: 120px; height: 120px; border: 3px solid {showSuccess ? '#22c55e' : primaryColor}; background-color: {isDark ? '#000' : '#fff'}; box-shadow: {isDark
? '0 6px 12px rgba(0, 0, 0, 0.4)'
: '0 6px 12px rgba(0, 0, 0, 0.15)'};"
role="img"
aria-label="{appName} logo"
>
{#if showSuccess}
<Icon name="check" size={55} color="#22c55e" />
{:else}
<Logo size={55} color={primaryColor} />
{/if}
</div>
<h1 class="text-2xl font-semibold" style="color: {isDark ? '#ffffff' : '#000000'};">
{appName}
</h1>
</div>
<!-- Middle Section - Auth Form -->
<div class="flex-1 flex items-start justify-center px-5 pt-8 pb-8">
<div
class="w-full max-w-md rounded-xl p-6"
class:shake={shakeError}
style="background-color: {isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"
>
<!-- Title -->
<div class="mb-6">
<h2
class="text-center text-xl font-semibold"
style="color: {isDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'};"
>
{t.title}
</h2>
<p
class="mt-2 text-sm text-center"
style="color: {isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};"
>
{t.subtitle}
</p>
</div>
<!-- Error Messages -->
{#if error}
<div
id="form-error"
role="alert"
aria-live="assertive"
class="mb-4 rounded-xl bg-red-500/20 border border-red-500/30 p-3 flex items-center gap-2"
>
<Icon name="warning" size={18} color="#ef4444" />
<p class="text-sm text-red-500">{error}</p>
</div>
{/if}
<!-- Login Form -->
<form
onsubmit={(e) => {
e.preventDefault();
handleLogin();
}}
class="pb-4"
aria-busy={loading}
aria-describedby={error ? 'form-error' : undefined}
>
<!-- Email Field -->
<div class="mb-3">
<label for="email" class="sr-only">{t.emailPlaceholder}</label>
<input
id="email"
type="email"
bind:this={emailInput}
bind:value={email}
placeholder={t.emailPlaceholder}
required
autocomplete="email"
aria-invalid={errorField === 'email'}
aria-describedby={errorField === 'email' ? 'form-error' : undefined}
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {errorField === 'email' ? '#ef4444' : (isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)')}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {errorField === 'email' ? '#ef4444' : primaryColor};"
/>
</div>
<!-- Password Field -->
<div class="mb-3 relative">
<label for="password" class="sr-only">{t.passwordPlaceholder}</label>
<input
id="password"
type={showPassword ? 'text' : 'password'}
bind:this={passwordInput}
bind:value={password}
placeholder={t.passwordPlaceholder}
required
autocomplete="current-password"
aria-invalid={errorField === 'password'}
aria-describedby={errorField === 'password' ? 'form-error' : undefined}
class="h-14 w-full rounded-xl border px-4 pr-12 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {errorField === 'password' ? '#ef4444' : (isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)')}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {errorField === 'password' ? '#ef4444' : primaryColor};"
/>
<button
type="button"
onclick={() => (showPassword = !showPassword)}
class="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg hover:bg-black/10 dark:hover:bg-white/10 transition-colors touch-target flex items-center justify-center"
aria-label={showPassword ? t.hidePassword : t.showPassword}
aria-pressed={showPassword}
title={showPassword ? t.hidePassword : t.showPassword}
>
<Icon
name={showPassword ? 'eye-off' : 'eye'}
size={20}
color={isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'}
/>
</button>
</div>
<!-- Remember Me & Forgot Password Row -->
<div class="mb-4 flex items-center justify-between">
<label class="flex items-center gap-2 cursor-pointer touch-target">
<input
type="checkbox"
bind:checked={rememberMe}
class="w-5 h-5 rounded border-2 transition-colors cursor-pointer"
style="accent-color: {primaryColor};"
/>
<span
class="text-sm"
style="color: {isDark ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.7)'};"
>
{t.rememberMe}
</span>
</label>
<button
type="button"
onclick={() => goto(forgotPasswordPath)}
class="text-sm font-medium transition-opacity hover:opacity-70 touch-target flex items-center justify-center px-2"
style="color: {primaryColor};"
>
{t.forgotPassword}
</button>
</div>
<!-- Submit Button -->
<button
type="submit"
disabled={loading || showSuccess}
aria-disabled={loading || showSuccess}
class="flex h-14 w-full items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 disabled:opacity-50 border-2 touch-target"
style="background-color: {showSuccess ? '#22c55e' : primaryColor + '60'}; border-color: {showSuccess ? '#22c55e' : primaryColor}; color: {isDark ? '#ffffff' : '#000000'};"
>
{#if loading}
<svg
class="spinner w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<circle cx="12" cy="12" r="10" stroke-opacity="0.25" />
<path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round" />
</svg>
<span>{t.signingIn}</span>
{:else if showSuccess}
<Icon name="check" size={20} />
<span>{t.success}</span>
{:else}
<Icon name="sign-in" size={20} />
<span>{t.signInButton}</span>
{/if}
</button>
</form>
<!-- Social Login -->
{#if enableGoogle || enableApple}
<div class="my-4 flex items-center gap-3" role="separator" aria-orientation="horizontal">
<div class="flex-1 border-t" style="border-color: {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"></div>
<span class="text-xs" style="color: {isDark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)'};">{t.orDivider}</span>
<div class="flex-1 border-t" style="border-color: {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"></div>
</div>
<div class="mb-4 flex flex-col gap-2" role="group" aria-label="Social login options">
{#if enableGoogle && onSignInWithGoogle}
<GoogleSignInButton onSuccess={handleGoogleSuccess} />
{/if}
{#if enableApple}
<AppleSignInButton />
{/if}
</div>
{/if}
<!-- Register Link -->
<div class="mt-4 text-center">
<p
class="text-sm"
style="color: {isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};"
>
{t.noAccount}
<button
type="button"
onclick={() => goto(registerPath)}
class="font-medium transition-opacity hover:opacity-70 touch-target inline-flex items-center justify-center px-1"
style="color: {primaryColor};"
>
{t.createAccount}
</button>
</p>
</div>
</div>
</div>
</main>
<!-- App Slider -->
{#if appSlider}
<div class="w-full pb-8 px-2 pt-4">
{@render appSlider()}
</div>
{:else}
<!-- Bottom padding -->
<div class="pb-8"></div>
{/if}
</div>

View file

@ -35,7 +35,8 @@
emailPlaceholder: 'Email',
passwordPlaceholder: 'Password',
confirmPasswordPlaceholder: 'Confirm Password',
passwordRequirements: 'Password must be at least 8 characters with lowercase, uppercase, number, and special character.',
passwordRequirements:
'Password must be at least 8 characters with lowercase, uppercase, number, and special character.',
createAccountButton: 'Create Account',
creatingAccount: 'Creating Account...',
backToLogin: 'Back to Login',
@ -46,9 +47,10 @@
confirmPasswordRequired: 'Please confirm your password',
passwordsDoNotMatch: 'Passwords do not match',
passwordTooShort: 'Password must be at least 8 characters',
passwordStrengthError: 'Password must include lowercase, uppercase, number, and special character',
passwordStrengthError:
'Password must include lowercase, uppercase, number, and special character',
registrationFailed: 'Registration failed',
accountCreated: 'Account created! Please check your email to verify your account.'
accountCreated: 'Account created! Please check your email to verify your account.',
};
interface Props {
@ -87,7 +89,7 @@
lightBackground = '#f5f5f5',
darkBackground = '#121212',
appSlider,
translations = {}
translations = {},
}: Props = $props();
// Merge provided translations with defaults
@ -126,7 +128,7 @@
lowercase: false,
uppercase: false,
digit: false,
special: false
special: false,
};
}
@ -135,7 +137,7 @@
lowercase: /[a-z]/.test(password),
uppercase: /[A-Z]/.test(password),
digit: /[0-9]/.test(password),
special: /[^a-zA-Z0-9]/.test(password)
special: /[^a-zA-Z0-9]/.test(password),
};
});
@ -222,7 +224,9 @@
<div class="flex flex-col items-center justify-center pt-16 pb-8">
<div
class="flex items-center justify-center rounded-full transition-all mb-4"
style="width: 120px; height: 120px; border: 3px solid {primaryColor}; background-color: {isDark ? '#000' : '#fff'}; box-shadow: {isDark
style="width: 120px; height: 120px; border: 3px solid {primaryColor}; background-color: {isDark
? '#000'
: '#fff'}; box-shadow: {isDark
? '0 6px 12px rgba(0, 0, 0, 0.4)'
: '0 6px 12px rgba(0, 0, 0, 0.15)'};"
>
@ -237,7 +241,11 @@
<div class="flex-1 flex items-start justify-center px-5 pt-8 pb-8">
<div
class="w-full max-w-md rounded-xl p-6"
style="background-color: {isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'};"
style="background-color: {isDark
? 'rgba(255, 255, 255, 0.08)'
: 'rgba(255, 255, 255, 0.7)'}; backdrop-filter: blur(10px); border: 1px solid {isDark
? 'rgba(255, 255, 255, 0.1)'
: 'rgba(0, 0, 0, 0.1)'};"
>
<!-- Title -->
<h2
@ -278,7 +286,13 @@
placeholder={t.emailPlaceholder}
required
class="h-14 w-full rounded-xl border px-4 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {primaryColor};"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {primaryColor};"
/>
</div>
@ -290,7 +304,13 @@
required
minlength={8}
class="h-14 w-full rounded-xl border px-4 pr-12 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {primaryColor};"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {primaryColor};"
/>
<button
type="button"
@ -314,7 +334,13 @@
required
minlength={8}
class="h-14 w-full rounded-xl border px-4 pr-12 text-lg transition-colors focus:outline-none focus:ring-2"
style="background-color: {isDark ? 'rgba(0, 0, 0, 0.2)' : 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.1)'}; color: {isDark ? '#ffffff' : '#000000'}; --tw-ring-color: {primaryColor};"
style="background-color: {isDark
? 'rgba(0, 0, 0, 0.2)'
: 'rgba(255, 255, 255, 0.8)'}; border-color: {isDark
? 'rgba(255, 255, 255, 0.2)'
: 'rgba(0, 0, 0, 0.1)'}; color: {isDark
? '#ffffff'
: '#000000'}; --tw-ring-color: {primaryColor};"
/>
<button
type="button"
@ -342,7 +368,9 @@
type="submit"
disabled={loading}
class="flex h-14 w-full items-center justify-center gap-2 rounded-xl font-medium transition-all hover:opacity-80 disabled:opacity-50 border-2"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark ? '#ffffff' : '#000000'};"
style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark
? '#ffffff'
: '#000000'};"
>
<Icon name="user-plus" size={20} />
{loading ? t.creatingAccount : t.createAccountButton}