-
+
+
-
{{ errors.password }}
+ required
+ />
+
+
+
+
+
+
+
{{ errors.username }}
+
{{ errors.password }}
+
+ {{ $t('auth.common.bothFieldsRequired') }}
+
-
+
+
+
+
{{ $t('auth.login.orContinueWith') }}
+
+
+
+
+
-
+
+
+
+
+
+
@@ -255,6 +313,52 @@ const goHome = () => {
router.push('/');
};
+// Third party login handlers
+const handleGoogleLogin = async () => {
+ isLoading.value = true;
+ try {
+ // Simulate Google OAuth flow
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ console.log('Google login initiated');
+ // In real implementation, redirect to Google OAuth
+ router.push('/');
+ } catch (error) {
+ console.error('Google login error:', error);
+ } finally {
+ isLoading.value = false;
+ }
+};
+
+const handleLineLogin = async () => {
+ isLoading.value = true;
+ try {
+ // Simulate LINE OAuth flow
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ console.log('LINE login initiated');
+ // In real implementation, redirect to LINE OAuth
+ router.push('/');
+ } catch (error) {
+ console.error('LINE login error:', error);
+ } finally {
+ isLoading.value = false;
+ }
+};
+
+const handleAppleLogin = async () => {
+ isLoading.value = true;
+ try {
+ // Simulate Apple Sign-In flow
+ await new Promise(resolve => setTimeout(resolve, 1500));
+ console.log('Apple login initiated');
+ // In real implementation, redirect to Apple Sign-In
+ router.push('/');
+ } catch (error) {
+ console.error('Apple login error:', error);
+ } finally {
+ isLoading.value = false;
+ }
+};
+
function toggleLanguageMenu() {
isLanguageMenuOpen.value = !isLanguageMenuOpen.value;
@@ -321,16 +425,28 @@ watch(locale, () => {
width: 100%;
height: 100%;
background: linear-gradient(135deg,
- #667eea 0%,
- #764ba2 25%,
- #f093fb 50%,
- #f5576c 75%,
- #4facfe 100%);
+ #f8fafc 0%,
+ #e2e8f0 25%,
+ #cbd5e1 50%,
+ #94a3b8 75%,
+ #64748b 100%);
background-size: 400% 400%;
animation: gradient-shift 15s ease infinite;
z-index: 0;
}
+.glass-layer {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ z-index: 1;
+}
+
@keyframes gradient-shift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
@@ -363,7 +479,7 @@ watch(locale, () => {
.lock-content {
position: relative;
- z-index: 1;
+ z-index: 2;
flex: 1;
display: flex;
flex-direction: column;
@@ -377,7 +493,7 @@ watch(locale, () => {
.time-section {
text-align: center;
- margin-bottom: 40px;
+ margin-bottom: 60px;
position: relative;
width: 100%;
display: flex;
@@ -390,7 +506,7 @@ watch(locale, () => {
font-size: 6rem;
font-weight: 200;
letter-spacing: -0.02em;
- margin-bottom: 8px;
+ margin-bottom: 12px;
text-shadow: 0 2px 20px rgba(0, 0, 0, 0.3);
animation: time-glow 2s ease-in-out infinite alternate;
line-height: 1;
@@ -425,13 +541,12 @@ watch(locale, () => {
display: flex;
flex-direction: column;
align-items: center;
- gap: 20px;
+ gap: 24px;
}
-
.avatar-circle {
- width: 120px;
- height: 120px;
+ width: 80px;
+ height: 80px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(20px);
@@ -450,7 +565,7 @@ watch(locale, () => {
}
.avatar-icon {
- font-size: 3rem;
+ font-size: 2rem;
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.3));
}
@@ -462,8 +577,9 @@ watch(locale, () => {
}
.username-input {
- width: 300px;
- padding: 16px 24px;
+ width: 280px;
+ height: 50px;
+ padding: 14px 24px;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border: 2px solid rgba(255, 255, 255, 0.3);
@@ -474,6 +590,8 @@ watch(locale, () => {
text-align: center;
outline: none;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
+ transition: all 0.3s ease;
+ box-sizing: border-box;
}
.username-input::placeholder {
@@ -482,9 +600,9 @@ watch(locale, () => {
}
.username-input:focus {
- background: rgba(255, 255, 255, 0.25);
- border-color: rgba(255, 255, 255, 0.6);
- box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.1);
+ background: rgba(255, 255, 255, 0.15);
+ border-color: rgba(255, 255, 255, 0.3);
+ outline: none;
}
.username-input.error {
@@ -513,30 +631,40 @@ watch(locale, () => {
.form {
display: flex;
align-items: center;
- gap: 16px;
justify-content: center;
position: relative;
}
.password-field {
position: relative;
-}
-
-.password-input {
- width: 300px;
- padding: 16px 24px;
+ display: flex;
+ align-items: center;
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(20px);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50px;
+ width: 280px;
+ height: 50px;
+ transition: all 0.3s ease;
+}
+
+.password-field:hover {
+ background: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.4);
+}
+
+.password-input {
+ width: 100%;
+ height: 100%;
+ padding: 14px 24px;
+ background: transparent;
+ border: none;
color: white;
font-size: 1.1rem;
font-weight: 300;
text-align: center;
- transition: all 0.3s ease;
outline: none;
- display: block;
- margin: 0 auto;
+ transition: all 0.3s ease;
}
.password-input::placeholder {
@@ -544,14 +672,17 @@ watch(locale, () => {
}
.password-input:focus {
- background: rgba(255, 255, 255, 0.25);
- border-color: rgba(255, 255, 255, 0.6);
- box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.1);
+ background: transparent;
+ border-color: rgba(255, 255, 255, 0.3);
+ outline: none;
}
-.password-input.error {
+.password-field.error {
border-color: #ff6b6b;
background: rgba(255, 107, 107, 0.2);
+}
+
+.password-field.error .password-input {
animation: shake 0.5s ease-in-out;
}
@@ -565,26 +696,38 @@ watch(locale, () => {
animation: shake 0.5s ease-in-out;
}
-.error-message {
- position: absolute;
- top: 100%;
- left: 50%;
- transform: translateX(-50%);
+.error-messages {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
margin-top: 12px;
+ min-height: 20px;
+}
+
+.error-message {
font-size: 0.9rem;
color: #ff6b6b;
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
+ text-align: center;
white-space: nowrap;
- z-index: 10;
+}
+
+.combined-error {
+ font-weight: 500;
+ color: #ff8a8a;
}
.login-button {
- width: 50px;
- height: 50px;
+ position: absolute;
+ right: 4px;
+ top: 50%;
+ transform: translateY(-50%);
+ width: 42px;
+ height: 42px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
- backdrop-filter: blur(20px);
- border: 2px solid rgba(255, 255, 255, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.3);
color: white;
font-size: 1.2rem;
cursor: pointer;
@@ -592,10 +735,7 @@ watch(locale, () => {
display: flex;
align-items: center;
justify-content: center;
- position: absolute;
- right: -70px;
- top: 50%;
- transform: translateY(-50%);
+ outline: none;
}
.login-button:hover:not(:disabled) {
@@ -626,8 +766,10 @@ watch(locale, () => {
.login-options {
display: flex;
- gap: 32px;
+ gap: 24px;
margin-top: 16px;
+ justify-content: center;
+ flex-wrap: wrap;
}
.option-link {
@@ -645,6 +787,99 @@ watch(locale, () => {
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}
+/* Divider */
+.divider {
+ display: flex;
+ align-items: center;
+ margin: 24px 0;
+ gap: 16px;
+}
+
+.divider-line {
+ flex: 1;
+ height: 1px;
+ background: rgba(255, 255, 255, 0.3);
+}
+
+.divider-text {
+ color: rgba(255, 255, 255, 0.7);
+ font-size: 0.9rem;
+ font-weight: 300;
+ white-space: nowrap;
+ padding: 0 8px;
+}
+
+/* Third Party Login */
+.third-party-login {
+ display: flex;
+ justify-content: center;
+ gap: 20px;
+ width: 100%;
+ margin: 0 auto;
+}
+
+.third-party-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 60px;
+ height: 60px;
+ background: rgba(255, 255, 255, 0.1);
+ backdrop-filter: blur(20px);
+ border: 2px solid rgba(255, 255, 255, 0.2);
+ border-radius: 50%;
+ color: white;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+ position: relative;
+ overflow: hidden;
+}
+
+.third-party-btn:hover:not(:disabled) {
+ background: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.4);
+ transform: translateY(-3px) scale(1.05);
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+}
+
+.third-party-btn:active:not(:disabled) {
+ transform: translateY(-1px) scale(1.02);
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
+}
+
+.third-party-btn:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.third-party-icon {
+ width: 24px;
+ height: 24px;
+ flex-shrink: 0;
+}
+
+/* Google Button */
+.google-btn:hover:not(:disabled) {
+ background: rgba(66, 133, 244, 0.25);
+ border-color: rgba(66, 133, 244, 0.5);
+ box-shadow: 0 10px 30px rgba(66, 133, 244, 0.3);
+}
+
+/* LINE Button */
+.line-btn:hover:not(:disabled) {
+ background: rgba(0, 185, 0, 0.25);
+ border-color: rgba(0, 185, 0, 0.5);
+ box-shadow: 0 10px 30px rgba(0, 185, 0, 0.3);
+}
+
+/* Apple Button */
+.apple-btn:hover:not(:disabled) {
+ background: rgba(0, 0, 0, 0.25);
+ border-color: rgba(255, 255, 255, 0.5);
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+}
+
.bottom-actions {
display: flex;
@@ -777,36 +1012,41 @@ watch(locale, () => {
/* Responsive Design */
@media (max-width: 768px) {
.lock-content {
- padding: 40px 20px 20px;
+ padding: 60px 20px 20px;
}
.current-time {
- font-size: 4rem;
+ font-size: 5rem;
}
.current-date {
- font-size: 1.2rem;
+ font-size: 1.4rem;
}
.avatar-circle {
- width: 100px;
- height: 100px;
+ width: 60px;
+ height: 60px;
}
.avatar-icon {
- font-size: 2.5rem;
+ font-size: 1.5rem;
+ }
+
+ .password-field {
+ width: 220px;
+ height: 44px;
}
.password-input {
- width: 250px;
- padding: 14px 20px;
- font-size: 1rem;
+ padding: 10px 16px;
+ font-size: 0.9rem;
}
.username-input {
- width: 250px;
- padding: 14px 20px;
- font-size: 1rem;
+ width: 220px;
+ height: 44px;
+ padding: 10px 16px;
+ font-size: 0.9rem;
}
.form {
@@ -815,28 +1055,32 @@ watch(locale, () => {
align-items: center;
}
- .password-field {
- position: relative;
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 12px;
- }
-
.login-button {
- position: static;
- transform: none;
- right: auto;
- top: auto;
- margin: 0;
+ width: 36px;
+ height: 36px;
+ font-size: 1rem;
}
.login-options {
flex-direction: column;
- gap: 16px;
+ gap: 12px;
text-align: center;
}
+ .third-party-login {
+ gap: 16px;
+ }
+
+ .third-party-btn {
+ width: 50px;
+ height: 50px;
+ }
+
+ .third-party-icon {
+ width: 20px;
+ height: 20px;
+ }
+
.bottom-actions {
gap: 30px;
}
@@ -850,48 +1094,51 @@ watch(locale, () => {
/* Extra small screens */
@media (max-width: 480px) {
.lock-content {
- padding: 20px 16px 16px;
+ padding: 40px 16px 16px;
}
.current-time {
- font-size: 3rem;
+ font-size: 4rem;
}
.current-date {
- font-size: 1rem;
+ font-size: 1.2rem;
}
.avatar-circle {
- width: 80px;
- height: 80px;
+ width: 50px;
+ height: 50px;
}
.avatar-icon {
- font-size: 2rem;
+ font-size: 1.2rem;
+ }
+
+ .password-field {
+ width: 180px;
+ height: 40px;
}
- .username-input,
.password-input {
- width: 200px;
- padding: 12px 16px;
- font-size: 0.9rem;
+ padding: 10px 14px;
+ font-size: 0.85rem;
+ }
+
+ .username-input {
+ width: 180px;
+ height: 40px;
+ padding: 10px 14px;
+ font-size: 0.85rem;
}
.form {
gap: 12px;
}
- .password-field {
- flex-direction: row;
- gap: 8px;
- justify-content: center;
- }
-
.login-button {
- width: 40px;
- height: 40px;
- font-size: 1rem;
- margin: 0;
+ width: 32px;
+ height: 32px;
+ font-size: 0.9rem;
}
.login-options {
@@ -901,17 +1148,35 @@ watch(locale, () => {
.option-link {
font-size: 0.8rem;
}
+
+ .third-party-login {
+ gap: 12px;
+ }
+
+ .third-party-btn {
+ width: 45px;
+ height: 45px;
+ }
+
+ .third-party-icon {
+ width: 18px;
+ height: 18px;
+ }
}
/* Dark mode adjustments */
@media (prefers-color-scheme: dark) {
.background-image {
background: linear-gradient(135deg,
- #1a1a2e 0%,
- #16213e 25%,
- #0f3460 50%,
- #533483 75%,
- #e94560 100%);
+ #1e293b 0%,
+ #334155 25%,
+ #475569 50%,
+ #64748b 75%,
+ #94a3b8 100%);
+ }
+
+ .glass-layer {
+ background: rgba(0, 0, 0, 0.1);
}
}
diff --git a/i18n/lang/en.json b/i18n/lang/en.json
index dd8694f..0a204e8 100644
--- a/i18n/lang/en.json
+++ b/i18n/lang/en.json
@@ -91,7 +91,9 @@
"signUp": "Sign Up",
"emailPlaceholder": "Enter your email",
"passwordPlaceholder": "Enter your password",
- "usernamePlaceholder": "Enter your username"
+ "usernamePlaceholder": "Enter your username",
+ "orContinueWith": "Or continue with",
+ "backToHome": "Back to Home"
},
"register": {
"title": "Register",
@@ -119,7 +121,8 @@
"invalidEmail": "Please enter a valid email address",
"passwordTooShort": "Password must be at least 6 characters",
"usernameTooShort": "Username must be at least 3 characters",
- "passwordsDoNotMatch": "Passwords do not match"
+ "passwordsDoNotMatch": "Passwords do not match",
+ "bothFieldsRequired": "Please enter both username and password"
}
},
"install": {
diff --git a/i18n/lang/zh-TW.json b/i18n/lang/zh-TW.json
index 2dac8c1..bf015e2 100644
--- a/i18n/lang/zh-TW.json
+++ b/i18n/lang/zh-TW.json
@@ -91,7 +91,9 @@
"signUp": "立即註冊",
"emailPlaceholder": "請輸入您的電子郵件",
"passwordPlaceholder": "請輸入您的密碼",
- "usernamePlaceholder": "請輸入您的用戶名"
+ "usernamePlaceholder": "請輸入您的用戶名",
+ "orContinueWith": "或使用以下方式繼續",
+ "backToHome": "返回首頁"
},
"register": {
"title": "註冊",
@@ -119,7 +121,8 @@
"invalidEmail": "請輸入有效的電子郵件地址",
"passwordTooShort": "密碼至少需要 6 個字符",
"usernameTooShort": "用戶名至少需要 3 個字符",
- "passwordsDoNotMatch": "密碼不匹配"
+ "passwordsDoNotMatch": "密碼不匹配",
+ "bothFieldsRequired": "請填寫用戶名和密碼"
}
},
"install": {