feat: Discord-style login page redesign
This commit is contained in:
+1
-2
@@ -19,5 +19,4 @@ data/
|
|||||||
packs/
|
packs/
|
||||||
.__pycache__
|
.__pycache__
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
.venv
|
.venv
|
||||||
resources
|
|
||||||
@@ -13,9 +13,9 @@
|
|||||||
<!-- Login Screen -->
|
<!-- Login Screen -->
|
||||||
<div id="login-screen" class="screen">
|
<div id="login-screen" class="screen">
|
||||||
<div class="login-container">
|
<div class="login-container">
|
||||||
<div class="login-brand">
|
<div class="login-header">
|
||||||
<div class="brand-icon">
|
<div class="brand-icon">
|
||||||
<svg width="56" height="56" viewBox="0 0 56 56" fill="none">
|
<svg width="40" height="40" viewBox="0 0 56 56" fill="none">
|
||||||
<rect width="56" height="56" rx="14" fill="url(#brandGrad)"/>
|
<rect width="56" height="56" rx="14" fill="url(#brandGrad)"/>
|
||||||
<path d="M18 28 L28 18 L38 28 L28 38 Z" fill="white" opacity="0.9"/>
|
<path d="M18 28 L28 18 L38 28 L28 38 Z" fill="white" opacity="0.9"/>
|
||||||
<defs>
|
<defs>
|
||||||
@@ -26,25 +26,28 @@
|
|||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<h1 class="brand-title">ZernMC</h1>
|
<h1 class="login-title" data-i18n="login.title">Sign In</h1>
|
||||||
<p class="brand-sub">Launcher <span id="version" data-i18n="version">1.0.11</span></p>
|
<p class="login-subtitle" data-i18n="login.subtitle">Welcome back to ZernMC</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="login-form" class="login-form">
|
<form id="login-form" class="login-form">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input type="text" id="username" placeholder="Username" data-i18n-placeholder="login.username" autocomplete="username" required>
|
|
||||||
<label for="username" data-i18n="login.username">Username</label>
|
<label for="username" data-i18n="login.username">Username</label>
|
||||||
|
<input type="text" id="username" autocomplete="username" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<input type="password" id="password" placeholder="Password" data-i18n-placeholder="login.password" autocomplete="current-password" required>
|
|
||||||
<label for="password" data-i18n="login.password">Password</label>
|
<label for="password" data-i18n="login.password">Password</label>
|
||||||
|
<input type="password" id="password" autocomplete="current-password" required>
|
||||||
</div>
|
</div>
|
||||||
<p id="login-error" class="error-msg hidden"></p>
|
<p id="login-error" class="error-msg hidden"></p>
|
||||||
<button type="submit" class="btn-primary" id="login-btn">
|
<button type="submit" class="btn-primary" id="login-btn">
|
||||||
<span class="btn-label" data-i18n="login.title">Sign In</span>
|
<span class="btn-label" data-i18n="login.title">Sign In</span>
|
||||||
<div class="spinner hidden"></div>
|
<div class="spinner hidden"></div>
|
||||||
</button>
|
</button>
|
||||||
<p class="login-hint" data-i18n="login.hint">New account will be created automatically on first login</p>
|
<div class="login-footer">
|
||||||
|
<span data-i18n="login.hint">Need an account?</span>
|
||||||
|
<span class="login-footer-action" data-i18n="login.autoHint">It's free — just sign in</span>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ const LOCALES = {
|
|||||||
en: {
|
en: {
|
||||||
'nav.packs': 'Packs', 'nav.news': 'News', 'nav.settings': 'Settings',
|
'nav.packs': 'Packs', 'nav.news': 'News', 'nav.settings': 'Settings',
|
||||||
'login.title': 'Sign In', 'login.username': 'Username', 'login.password': 'Password',
|
'login.title': 'Sign In', 'login.username': 'Username', 'login.password': 'Password',
|
||||||
'login.hint': 'New account will be created automatically on first login',
|
'login.subtitle': 'Welcome back to ZernMC',
|
||||||
|
'login.hint': 'Need an account?',
|
||||||
|
'login.autoHint': "It's free \u2014 just sign in",
|
||||||
'login.signingIn': 'Signing in...',
|
'login.signingIn': 'Signing in...',
|
||||||
'loading.text': 'Loading...',
|
'loading.text': 'Loading...',
|
||||||
'sidebar.serverPacks': 'Server Packs', 'sidebar.localPacks': 'Local Packs',
|
'sidebar.serverPacks': 'Server Packs', 'sidebar.localPacks': 'Local Packs',
|
||||||
@@ -180,7 +182,9 @@ const LOCALES = {
|
|||||||
ru: {
|
ru: {
|
||||||
'nav.packs': 'Сборки', 'nav.news': 'Новости', 'nav.settings': 'Настройки',
|
'nav.packs': 'Сборки', 'nav.news': 'Новости', 'nav.settings': 'Настройки',
|
||||||
'login.title': 'Вход', 'login.username': 'Логин', 'login.password': 'Пароль',
|
'login.title': 'Вход', 'login.username': 'Логин', 'login.password': 'Пароль',
|
||||||
'login.hint': 'Новый аккаунт создаётся автоматически при первом входе',
|
'login.subtitle': 'С возвращением в ZernMC',
|
||||||
|
'login.hint': 'Нет аккаунта?',
|
||||||
|
'login.autoHint': 'Это бесплатно \u2014 просто войдите',
|
||||||
'login.signingIn': 'Вход...',
|
'login.signingIn': 'Вход...',
|
||||||
'loading.text': 'Загрузка...',
|
'loading.text': 'Загрузка...',
|
||||||
'sidebar.serverPacks': 'Серверные сборки', 'sidebar.localPacks': 'Локальные сборки',
|
'sidebar.serverPacks': 'Серверные сборки', 'sidebar.localPacks': 'Локальные сборки',
|
||||||
|
|||||||
@@ -60,84 +60,75 @@ body {
|
|||||||
/* ========== LOGIN ========== */
|
/* ========== LOGIN ========== */
|
||||||
.login-container {
|
.login-container {
|
||||||
background: var(--bg-elevated);
|
background: var(--bg-elevated);
|
||||||
border: 1px solid var(--border);
|
border-radius: var(--radius-md);
|
||||||
border-radius: var(--radius-lg);
|
padding: 32px 32px 24px;
|
||||||
padding: 48px 40px 40px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 380px;
|
max-width: 360px;
|
||||||
box-shadow: var(--shadow);
|
box-shadow: 0 8px 40px rgba(0,0,0,0.5);
|
||||||
animation: floatIn 0.5s ease forwards;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes floatIn {
|
.login-header { text-align: center; margin-bottom: 28px; }
|
||||||
from { opacity: 0; transform: translateY(20px); }
|
.brand-icon { margin-bottom: 12px; }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
.login-title {
|
||||||
}
|
font-size: 22px; font-weight: 700;
|
||||||
|
|
||||||
.login-brand { text-align: center; margin-bottom: 36px; }
|
|
||||||
.brand-icon { margin-bottom: 16px; }
|
|
||||||
.brand-title {
|
|
||||||
font-size: 28px; font-weight: 800;
|
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
}
|
}
|
||||||
.brand-sub { color: var(--text-muted); font-size: 13px; margin-top: 4px; }
|
.login-subtitle { color: var(--text-muted); font-size: 13px; margin-top: 4px; }
|
||||||
|
|
||||||
.login-form { display: flex; flex-direction: column; gap: 20px; }
|
.login-form { display: flex; flex-direction: column; gap: 16px; }
|
||||||
|
|
||||||
.field { position: relative; }
|
.field { display: flex; flex-direction: column; gap: 6px; }
|
||||||
.field label {
|
.field label {
|
||||||
position: absolute; top: 50%; left: 14px; transform: translateY(-50%);
|
font-size: 11px; font-weight: 700;
|
||||||
font-size: 13px; color: var(--text-muted);
|
text-transform: uppercase; letter-spacing: 0.5px;
|
||||||
transition: var(--transition); pointer-events: none;
|
color: var(--text-secondary);
|
||||||
background: var(--bg-elevated); padding: 0 4px;
|
|
||||||
}
|
|
||||||
.field input:focus + label,
|
|
||||||
.field input:not(:placeholder-shown) + label,
|
|
||||||
.field textarea:focus + label,
|
|
||||||
.field textarea:not(:placeholder-shown) + label {
|
|
||||||
top: 0; font-size: 11px; color: var(--accent);
|
|
||||||
}
|
}
|
||||||
.field input {
|
.field input {
|
||||||
width: 100%; padding: 14px 14px; font-size: 14px;
|
width: 100%; padding: 10px 12px; font-size: 15px;
|
||||||
background: var(--bg-surface); border: 1px solid var(--border-light);
|
background: var(--bg-surface); border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-sm); color: var(--text);
|
border-radius: 4px; color: var(--text);
|
||||||
font-family: var(--font); transition: var(--transition);
|
font-family: var(--font); transition: border-color 150ms ease, box-shadow 150ms ease;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
.field input:focus {
|
.field input:focus {
|
||||||
border-color: var(--accent);
|
border-color: var(--accent);
|
||||||
box-shadow: 0 0 0 3px var(--accent-glow);
|
box-shadow: 0 0 0 2px var(--accent-glow);
|
||||||
}
|
}
|
||||||
.field select {
|
.field select {
|
||||||
width: 100%; padding: 12px 14px; font-size: 14px;
|
width: 100%; padding: 10px 12px; font-size: 15px;
|
||||||
background: var(--bg-surface); border: 1px solid var(--border-light);
|
background: var(--bg-surface); border: 1px solid var(--border);
|
||||||
border-radius: var(--radius-sm); color: var(--text);
|
border-radius: 4px; color: var(--text);
|
||||||
font-family: var(--font); cursor: pointer; outline: none;
|
font-family: var(--font); cursor: pointer; outline: none;
|
||||||
}
|
}
|
||||||
.field select:focus { border-color: var(--accent); }
|
.field select:focus { border-color: var(--accent); }
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
width: 100%; padding: 14px; border: none; border-radius: var(--radius-sm);
|
width: 100%; padding: 14px; border: none; border-radius: 4px;
|
||||||
background: linear-gradient(135deg, var(--accent), #ff6b6b);
|
background: var(--accent); color: #fff;
|
||||||
color: #fff; font-size: 15px; font-weight: 600; cursor: pointer;
|
font-size: 14px; font-weight: 600; cursor: pointer;
|
||||||
font-family: var(--font); transition: var(--transition);
|
font-family: var(--font); transition: background 150ms ease, box-shadow 150ms ease;
|
||||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||||
min-height: 48px; position: relative;
|
min-height: 44px; position: relative;
|
||||||
}
|
}
|
||||||
.btn-primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-glow); }
|
.btn-primary:hover { background: #d63850; }
|
||||||
.btn-primary:active { transform: translateY(0); }
|
.btn-primary:active { background: #c22f46; transform: scale(0.98); }
|
||||||
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; box-shadow: none; }
|
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
|
||||||
|
|
||||||
.error-msg {
|
.error-msg {
|
||||||
color: var(--error); font-size: 13px; text-align: center;
|
color: var(--error); font-size: 13px; text-align: center;
|
||||||
padding: 10px; background: rgba(248,113,113,0.1);
|
padding: 10px; background: rgba(248,113,113,0.1);
|
||||||
border-radius: var(--radius-sm); animation: shake 0.4s ease;
|
border-radius: 4px; animation: shake 0.4s ease;
|
||||||
}
|
}
|
||||||
@keyframes shake {
|
@keyframes shake {
|
||||||
0%,100%{transform:translateX(0)}20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(3px)}
|
0%,100%{transform:translateX(0)}20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(3px)}
|
||||||
}
|
}
|
||||||
|
|
||||||
.login-hint { text-align: center; font-size: 12px; color: var(--text-muted); margin-top: 4px; }
|
.login-footer {
|
||||||
|
display: flex; justify-content: center; gap: 4px;
|
||||||
|
font-size: 12px; color: var(--text-muted); margin-top: 4px;
|
||||||
|
}
|
||||||
|
.login-footer-action { color: var(--accent); cursor: pointer; }
|
||||||
|
.login-footer-action:hover { text-decoration: underline; }
|
||||||
|
|
||||||
.spinner {
|
.spinner {
|
||||||
position: absolute; width: 20px; height: 20px;
|
position: absolute; width: 20px; height: 20px;
|
||||||
|
|||||||
Reference in New Issue
Block a user