From 65ea12a47beb5f15c23938f9e35d0091efb42241 Mon Sep 17 00:00:00 2001 From: Clawd Date: Sat, 28 Feb 2026 22:59:08 +0100 Subject: [PATCH] feat(auth): polish login/register with logo, gradients and animations --- frontend/src/App.css | 166 ++++++++++++++++++++++++++++ frontend/src/components/Logo.jsx | 9 ++ frontend/src/index.css | 36 ++++++ frontend/src/pages/LoginPage.jsx | 8 +- frontend/src/pages/RegisterPage.jsx | 8 +- 5 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/Logo.jsx diff --git a/frontend/src/App.css b/frontend/src/App.css index 12f7616..3275ad8 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -2601,3 +2601,169 @@ .start-btn:active { transform: translateY(0); } + +/* ============================================================ + AUTH PAGES — Login / Register + ============================================================ */ + +@keyframes authFadeIn { + from { opacity: 0; transform: translateY(16px); } + to { opacity: 1; transform: translateY(0); } +} + +@keyframes errorShake { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(-6px); } + 40% { transform: translateX(6px); } + 60% { transform: translateX(-4px); } + 80% { transform: translateX(4px); } +} + +@keyframes errorFadeIn { + from { opacity: 0; transform: translateY(-4px); } + to { opacity: 1; transform: translateY(0); } +} + +.auth-page { + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + padding: var(--space-6); + background: + radial-gradient(ellipse 80% 50% at 50% -10%, rgba(255, 107, 74, 0.12) 0%, transparent 60%), + var(--bg-primary); + animation: authFadeIn var(--transition-slow) ease both; +} + +.auth-card { + width: 100%; + max-width: 400px; + background: var(--bg-card); + border: 1px solid var(--border-hover); + border-radius: var(--radius-2xl); + padding: var(--space-10) var(--space-8); + box-shadow: var(--shadow-xl), 0 0 40px rgba(255, 107, 74, 0.06); + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-4); +} + +.logo-mark { + width: 56px; + height: 56px; + color: var(--accent); + filter: drop-shadow(0 0 10px var(--accent-glow)); + flex-shrink: 0; +} + +.auth-title { + font-size: var(--font-2xl); + font-weight: 700; + color: var(--text-primary); + text-align: center; + margin: 0; +} + +.auth-tagline { + font-size: var(--font-sm); + color: var(--text-muted); + text-align: center; + margin: 0; + letter-spacing: 0.02em; +} + +.auth-card form { + width: 100%; + display: flex; + flex-direction: column; + gap: var(--space-3); + margin-top: var(--space-2); +} + +.auth-card input[type="email"], +.auth-card input[type="password"] { + width: 100%; + background: var(--bg-elevated); + border: 1.5px solid var(--border); + border-radius: var(--radius-lg); + padding: var(--space-4) var(--space-5); + color: var(--text-primary); + font-size: 16px; /* prevents iOS auto-zoom */ + transition: border-color var(--transition-base), box-shadow var(--transition-base); +} + +.auth-card input[type="email"]:focus, +.auth-card input[type="password"]:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-subtle); + outline: none; +} + +.auth-card input::placeholder { + color: var(--text-tertiary); +} + +.auth-card button[type="submit"] { + width: 100%; + padding: var(--space-4); + background: var(--accent); + color: white; + border: none; + border-radius: var(--radius-lg); + font-size: var(--font-base); + font-weight: 600; + cursor: pointer; + min-height: 52px; + margin-top: var(--space-2); + transition: background var(--transition-base), transform var(--transition-fast), box-shadow var(--transition-base); + box-shadow: 0 4px 14px rgba(255, 107, 74, 0.3); +} + +.auth-card button[type="submit"]:hover:not(:disabled) { + background: var(--accent-hover); + transform: translateY(-1px); + box-shadow: 0 6px 20px rgba(255, 107, 74, 0.45); +} + +.auth-card button[type="submit"]:active:not(:disabled) { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(255, 107, 74, 0.3); +} + +.auth-card button[type="submit"]:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.auth-error { + width: 100%; + background: var(--error-subtle); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: var(--radius-md); + padding: var(--space-3) var(--space-4); + color: #f87171; + font-size: var(--font-sm); + text-align: center; + animation: errorFadeIn var(--transition-base) ease both, + errorShake 400ms ease 100ms both; +} + +.auth-link { + font-size: var(--font-sm); + color: var(--text-muted); + text-align: center; +} + +.auth-link a { + color: var(--accent); + text-decoration: none; + font-weight: 500; + transition: color var(--transition-fast); +} + +.auth-link a:hover { + color: var(--accent-hover); + text-decoration: underline; +} diff --git a/frontend/src/components/Logo.jsx b/frontend/src/components/Logo.jsx new file mode 100644 index 0000000..ca6dff5 --- /dev/null +++ b/frontend/src/components/Logo.jsx @@ -0,0 +1,9 @@ +export default function Logo() { + return ( + + ); +} diff --git a/frontend/src/index.css b/frontend/src/index.css index 4f193a0..261facc 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -210,6 +210,42 @@ input { font-size: var(--font-lg); } +.logo-mark { + width: 56px; + height: 56px; + color: var(--accent); + margin: 0 auto var(--space-4); + display: block; +} + +.auth-title { + font-size: var(--font-2xl); + font-weight: 700; + color: var(--text-primary); + margin-bottom: var(--space-1); +} + +.auth-tagline { + color: var(--text-secondary); + font-size: var(--font-sm); + margin-bottom: var(--space-6); +} + +@keyframes auth-error-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.auth-error { + animation: auth-error-in 0.2s ease-out; +} + .auth-card form { display: flex; flex-direction: column; diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx index a882ec5..0c44fa0 100644 --- a/frontend/src/pages/LoginPage.jsx +++ b/frontend/src/pages/LoginPage.jsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; +import Logo from '../components/Logo'; export default function LoginPage() { const [email, setEmail] = useState(''); @@ -26,9 +27,10 @@ export default function LoginPage() { return (
-

🏋️ Gravl

-

Logga in

- {error &&
{error}
} + +

Logga in

+

Din personliga träningspartner

+ {error &&
{error}
}
setEmail(e.target.value)} required /> setPassword(e.target.value)} required /> diff --git a/frontend/src/pages/RegisterPage.jsx b/frontend/src/pages/RegisterPage.jsx index 57ecbcb..a729651 100644 --- a/frontend/src/pages/RegisterPage.jsx +++ b/frontend/src/pages/RegisterPage.jsx @@ -1,6 +1,7 @@ import { useState } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; +import Logo from '../components/Logo'; export default function RegisterPage() { const [email, setEmail] = useState(''); @@ -26,9 +27,10 @@ export default function RegisterPage() { return (
-

🏋️ Gravl

-

Skapa konto

- {error &&
{error}
} + +

Skapa konto

+

Börja din träningsresa

+ {error &&
{error}
} setEmail(e.target.value)} required /> setPassword(e.target.value)} required minLength={6} />