aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/client/pages/LoginPage.tsx
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-12-08 00:18:03 +0900
committernsfisis <nsfisis@gmail.com>2025-12-08 00:18:03 +0900
commit65c0adfd769b9ef11b897c96a3634c61120055b8 (patch)
tree74668feef8f134c1b132beaab125e42fa9d77b2e /src/client/pages/LoginPage.tsx
parent7cf55a3b7e37971ea0835118a26f032d895ff71f (diff)
downloadkioku-65c0adfd769b9ef11b897c96a3634c61120055b8.tar.gz
kioku-65c0adfd769b9ef11b897c96a3634c61120055b8.tar.zst
kioku-65c0adfd769b9ef11b897c96a3634c61120055b8.zip
feat(client): redesign frontend with TailwindCSS v4
Replace inline styles with TailwindCSS, implementing a cohesive Japanese-inspired design system with custom colors (cream, teal primary), typography (Fraunces, DM Sans), and animations. Update all pages and components with consistent styling, improve accessibility by adding aria-hidden to decorative SVGs, and configure Biome for Tailwind CSS syntax support. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'src/client/pages/LoginPage.tsx')
-rw-r--r--src/client/pages/LoginPage.tsx139
1 files changed, 105 insertions, 34 deletions
diff --git a/src/client/pages/LoginPage.tsx b/src/client/pages/LoginPage.tsx
index cc59105..89dd053 100644
--- a/src/client/pages/LoginPage.tsx
+++ b/src/client/pages/LoginPage.tsx
@@ -38,42 +38,113 @@ export function LoginPage() {
};
return (
- <div>
- <h1>Login</h1>
- <form onSubmit={handleSubmit}>
- {error && (
- <div role="alert" style={{ color: "red" }}>
- {error}
- </div>
- )}
- <div>
- <label htmlFor="username">Username</label>
- <input
- id="username"
- type="text"
- value={username}
- onChange={(e) => setUsername(e.target.value)}
- required
- autoComplete="username"
- disabled={isSubmitting}
- />
+ <div className="min-h-screen flex items-center justify-center px-4 py-12 bg-cream">
+ <div className="w-full max-w-sm animate-slide-up">
+ {/* Logo/Brand */}
+ <div className="text-center mb-10">
+ <h1 className="font-display text-4xl font-semibold text-ink tracking-tight">
+ Kioku
+ </h1>
+ <p className="mt-2 text-muted text-sm">Your memory, amplified</p>
</div>
- <div>
- <label htmlFor="password">Password</label>
- <input
- id="password"
- type="password"
- value={password}
- onChange={(e) => setPassword(e.target.value)}
- required
- autoComplete="current-password"
- disabled={isSubmitting}
- />
+
+ {/* Login Card */}
+ <div className="bg-white rounded-2xl shadow-lg p-8 border border-border/50">
+ <h2 className="font-display text-xl font-medium text-slate mb-6">
+ Welcome back
+ </h2>
+
+ <form onSubmit={handleSubmit} className="space-y-5">
+ {error && (
+ <div
+ role="alert"
+ className="bg-error/5 text-error text-sm px-4 py-3 rounded-lg border border-error/20"
+ >
+ {error}
+ </div>
+ )}
+
+ <div>
+ <label
+ htmlFor="username"
+ className="block text-sm font-medium text-slate mb-1.5"
+ >
+ Username
+ </label>
+ <input
+ id="username"
+ type="text"
+ value={username}
+ onChange={(e) => setUsername(e.target.value)}
+ required
+ autoComplete="username"
+ disabled={isSubmitting}
+ className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed"
+ placeholder="Enter your username"
+ />
+ </div>
+
+ <div>
+ <label
+ htmlFor="password"
+ className="block text-sm font-medium text-slate mb-1.5"
+ >
+ Password
+ </label>
+ <input
+ id="password"
+ type="password"
+ value={password}
+ onChange={(e) => setPassword(e.target.value)}
+ required
+ autoComplete="current-password"
+ disabled={isSubmitting}
+ className="w-full px-4 py-2.5 bg-ivory border border-border rounded-lg text-slate placeholder-muted transition-all duration-200 hover:border-muted focus:border-primary focus:ring-2 focus:ring-primary/10 disabled:opacity-50 disabled:cursor-not-allowed"
+ placeholder="Enter your password"
+ />
+ </div>
+
+ <button
+ type="submit"
+ disabled={isSubmitting}
+ className="w-full bg-primary hover:bg-primary-dark text-white font-medium py-2.5 px-4 rounded-lg transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed active:scale-[0.98] shadow-sm hover:shadow-md"
+ >
+ {isSubmitting ? (
+ <span className="flex items-center justify-center gap-2">
+ <svg
+ className="animate-spin h-4 w-4"
+ viewBox="0 0 24 24"
+ aria-hidden="true"
+ >
+ <circle
+ className="opacity-25"
+ cx="12"
+ cy="12"
+ r="10"
+ stroke="currentColor"
+ strokeWidth="4"
+ fill="none"
+ />
+ <path
+ className="opacity-75"
+ fill="currentColor"
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
+ />
+ </svg>
+ Signing in...
+ </span>
+ ) : (
+ "Sign in"
+ )}
+ </button>
+ </form>
</div>
- <button type="submit" disabled={isSubmitting}>
- {isSubmitting ? "Logging in..." : "Login"}
- </button>
- </form>
+
+ {/* Footer note */}
+ <p className="text-center text-muted text-xs mt-6">
+ Spaced repetition learning
+ </p>
+ </div>
</div>
);
}