style(landing): polish the public index, login and register screens
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Failing after 26s
CI / tests-unit (push) Skipped
CI / tests-integration (push) Skipped
CI / tests-ui (push) Skipped
CI / preflight (push) Skipped
CI / deploy (push) Skipped

- add theme-aware helpers: btn-brand, btn-glass-dark, auth-input, auth-label,
  auth-alert, explore-pill, avatar-tile, aurora blobs and a hero scroll cue
- reorder backdrop-filter declarations so the glass blur survives the
  production CSS optimizer in modern Chromium
- hero: aurora glow, frosted recent-users chip, premium CTA buttons and cue
- explore nav: icon pills with hover arrows; features: gradient icon tiles
- stats: single glass panel with column dividers; join CTA: aurora + ring
- auth forms: visible labels, icon inputs, eye/password toggle, gradient
  submit and pill footer links
- auth pages: gradient card frame, aurora accents on the intro panel and
  hover-lift avatar tiles; unified pill-shaped top bar
- drop the hard-coded register banner image in favor of the framed card
This commit is contained in:
openhands committed 2026-10-06 22:00:02 +02:00
1 parent 5b2eb91c5c
commit 57710a7fe3
8 files changed
+879 -521

No files matched your search

+95 -159
View File
@@ -1,6 +1,6 @@
"use client";
import Image from "next/image";
import { Lock, Mail, UserRound } from "lucide-react";
import Script from "next/script";
import { signIn } from "next-auth/react";
import { useTranslations } from "next-intl";
@@ -10,6 +10,7 @@ import {
type RegisterState,
register,
} from "@/actions/register";
import { PasswordToggle } from "@/components/auth/password-toggle";
import Link from "@/components/link";
import { signInWithTransientRetry } from "@/lib/auth/sign-in-retry";
@@ -142,56 +143,6 @@ export function RegisterForm({
nonce={nonce}
/>
) : null}
{/* Header Banner */}
<div
className="relative overflow-hidden bg-center bg-cover rounded-t-xl"
style={{
backgroundImage: "url(/assets/images/mybobba_banner.png)",
height: "160px",
}}
>
<div
className="absolute inset-0 z-10"
style={{
background:
"linear-gradient(180deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.55) 100%)",
}}
/>
<div className="relative flex items-center justify-center w-full h-full z-20">
<div
className="relative animate-float"
style={{ width: "140px", height: "140px" }}
>
<div
className="absolute inset-0 rounded-full overflow-hidden"
style={{ zIndex: -1 }}
>
<div
className="w-full h-full"
style={{
background: "black",
filter: "blur(8px)",
transform: "scale(1.2)",
opacity: 0.6,
}}
/>
</div>
<Image
src="/assets/images/FrankwithBag.gif"
alt=""
fill
className="!static"
style={{
objectFit: "contain",
objectPosition: "center",
width: "100%",
height: "100%",
}}
/>
</div>
</div>
</div>
{/* Form */}
<form
action={(fd) => {
@@ -201,33 +152,27 @@ export function RegisterForm({
};
formAction(fd);
}}
className="p-6 flex flex-col gap-6"
style={{
backgroundColor:
"color-mix(in srgb, var(--color-background) 50%, var(--color-surface))",
borderRadius: "0 0 12px 12px",
}}
className="flex flex-col gap-6"
>
{errorMessage && (
<div
<p
role="alert"
aria-live="polite"
className="animate-fade-in-up p-3 rounded-lg text-sm font-semibold text-primary-foreground"
style={{ backgroundColor: "var(--color-danger)" }}
className="auth-alert animate-fade-in-up m-0"
>
{errorMessage}
</div>
</p>
)}
{referralCode && (
<div
className="animate-fade-in-up flex items-center gap-2 p-3 rounded-lg text-sm font-semibold"
className="animate-fade-in-up flex items-center gap-2 rounded-xl border px-3 py-2.5 text-sm font-semibold"
style={{
backgroundColor:
background:
"color-mix(in srgb, var(--color-primary) 12%, transparent)",
color: "var(--color-primary-readable, var(--color-primary))",
border:
"1px solid color-mix(in srgb, var(--color-primary) 22%, transparent)",
borderColor:
"color-mix(in srgb, var(--color-primary) 26%, transparent)",
}}
>
{t("invitedBy", { username: referralCode })}
@@ -257,51 +202,45 @@ export function RegisterForm({
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
<div className="space-y-1.5">
<label
htmlFor="username"
className="block text-xs font-bold uppercase tracking-wider"
style={{ color: "var(--color-text-muted)" }}
>
<label htmlFor="username" className="auth-label">
{t("username")}
</label>
<input
id="username"
name="username"
type="text"
placeholder={t("usernamePlaceholder")}
autoComplete="username"
className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none"
style={{
backgroundColor: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
required
/>
<div className="relative">
<UserRound
aria-hidden="true"
className="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2"
style={{ color: "var(--color-text-muted)" }}
/>
<input
id="username"
name="username"
type="text"
placeholder={t("usernamePlaceholder")}
autoComplete="username"
className="auth-input has-icon"
required
/>
</div>
</div>
<div className="space-y-1.5">
<label
htmlFor="mail"
className="block text-xs font-bold uppercase tracking-wider"
style={{ color: "var(--color-text-muted)" }}
>
<label htmlFor="mail" className="auth-label">
{t("email")}
</label>
<input
id="mail"
name="mail"
type="email"
placeholder={t("emailPlaceholder")}
autoComplete="email"
className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none"
style={{
backgroundColor: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
/>
<div className="relative">
<Mail
aria-hidden="true"
className="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2"
style={{ color: "var(--color-text-muted)" }}
/>
<input
id="mail"
name="mail"
type="email"
placeholder={t("emailPlaceholder")}
autoComplete="email"
className="auth-input has-icon"
/>
</div>
</div>
</div>
</div>
@@ -327,14 +266,15 @@ export function RegisterForm({
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 stagger-children">
<div className="space-y-1.5">
<label
htmlFor="password"
className="block text-xs font-bold uppercase tracking-wider"
style={{ color: "var(--color-text-muted)" }}
>
<label htmlFor="password" className="auth-label">
{t("password")}
</label>
<div className="relative">
<Lock
aria-hidden="true"
className="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2"
style={{ color: "var(--color-text-muted)" }}
/>
<input
id="password"
name="password"
@@ -343,28 +283,19 @@ export function RegisterForm({
autoComplete="new-password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none"
style={{
backgroundColor: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
className="auth-input has-icon has-action"
required
/>
<button
type="button"
onClick={() => setShowPassword(!showPassword)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-sm font-bold opacity-60 hover:opacity-100 transition-opacity"
style={{ color: "var(--color-text-muted)" }}
tabIndex={-1}
>
{showPassword ? t("hidePassword") : t("showPassword")}
</button>
<PasswordToggle
show={showPassword}
onToggle={() => setShowPassword((v) => !v)}
labelShow={t("showPassword")}
labelHide={t("hidePassword")}
/>
</div>
{password.length > 0 && (
<div className="animate-fade-in-up space-y-1">
<div className="flex gap-1">
<div className="animate-fade-in-up space-y-1.5 pt-0.5">
<div className="flex gap-1.5">
{[1, 2, 3, 4, 5].map((i) => (
<div
key={i}
@@ -373,13 +304,17 @@ export function RegisterForm({
backgroundColor:
i <= strength.score
? strength.color
: "color-mix(in srgb, var(--color-text-muted) 15%, transparent)",
: "color-mix(in srgb, var(--color-text-muted) 18%, transparent)",
boxShadow:
i <= strength.score
? `0 0 8px color-mix(in srgb, ${strength.color} 45%, transparent)`
: undefined,
}}
/>
))}
</div>
<p
className="text-xs font-bold"
className="text-[11px] font-extrabold uppercase tracking-wider"
style={{ color: strength.color }}
>
{strength.label}
@@ -388,38 +323,30 @@ export function RegisterForm({
)}
</div>
<div className="space-y-1.5">
<label
htmlFor="password_confirmation"
className="block text-xs font-bold uppercase tracking-wider"
style={{ color: "var(--color-text-muted)" }}
>
<label htmlFor="password_confirmation" className="auth-label">
{t("confirmPassword")}
</label>
<div className="relative">
<Lock
aria-hidden="true"
className="pointer-events-none absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2"
style={{ color: "var(--color-text-muted)" }}
/>
<input
id="password_confirmation"
name="password_confirmation"
type={showConfirm ? "text" : "password"}
placeholder={t("confirmPasswordPlaceholder")}
autoComplete="new-password"
className="input-glow w-full rounded-xl border-2 px-4 py-3 text-sm font-medium transition-all focus:outline-none"
style={{
backgroundColor: "var(--color-background)",
color: "var(--color-text-readable)",
borderColor:
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
}}
className="auth-input has-icon has-action"
required
/>
<button
type="button"
onClick={() => setShowConfirm(!showConfirm)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-sm font-bold opacity-60 hover:opacity-100 transition-opacity"
style={{ color: "var(--color-text-muted)" }}
tabIndex={-1}
>
{showConfirm ? t("hidePassword") : t("showPassword")}
</button>
<PasswordToggle
show={showConfirm}
onToggle={() => setShowConfirm((v) => !v)}
labelShow={t("showPassword")}
labelHide={t("hidePassword")}
/>
</div>
</div>
</div>
@@ -556,15 +483,13 @@ export function RegisterForm({
});
}
}}
className={`btn-shine w-full rounded-xl font-extrabold text-sm py-3.5 transition-all duration-200 hover:scale-[1.02] active:scale-95 shadow-lg disabled:opacity-60 ${
termsAccepted ? "" : "opacity-70 saturate-[0.85] hover:scale-100"
className={`btn-brand btn-shine w-full py-3.5 text-sm font-extrabold uppercase tracking-wider disabled:opacity-60 ${
termsAccepted ? "" : "opacity-70 saturate-[0.85]"
}`}
style={{
background: "var(--color-primary)",
color: "var(--color-primary-foreground)",
boxShadow: termsAccepted
? "0 4px 24px color-mix(in srgb, var(--color-primary) 35%, transparent)"
: "0 2px 10px color-mix(in srgb, var(--color-primary) 12%, transparent)",
? undefined
: "0 4px 14px -8px color-mix(in srgb, var(--color-primary) 50%, transparent)",
}}
>
{isPending || signingIn ? (
@@ -597,14 +522,25 @@ export function RegisterForm({
)}
</button>
<div className="text-center">
<div
className="border-t pt-4 text-center"
style={{
borderColor:
"color-mix(in srgb, var(--color-text-muted) 14%, transparent)",
}}
>
<Link
href="/login"
className="text-sm font-bold hover:underline transition-all"
className="inline-flex items-center gap-2 rounded-full border px-5 py-2.5 text-xs font-extrabold transition-all duration-200 hover:-translate-y-0.5"
style={{
color: "var(--color-primary-readable, var(--color-primary))",
color: "var(--color-primary)",
background:
"color-mix(in srgb, var(--color-primary) 8%, transparent)",
borderColor:
"color-mix(in srgb, var(--color-primary) 35%, transparent)",
}}
>
<UserRound aria-hidden="true" className="h-3.5 w-3.5" />
{t("alreadyHaveAccount")}
</Link>
</div>