From 970528b5466146465c80ab1a0179a0589ca67092 Mon Sep 17 00:00:00 2001 From: host Date: Sat, 25 Jul 2026 04:15:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/styles.css | 413 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 410 insertions(+), 3 deletions(-) diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 7c617ac..38a851a 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -1,9 +1,416 @@ +/* ============================================================ + Proxmox VPS Panel — UI-стили + Тёмная тема, аккуратные карточки, читаемая типографика. + ============================================================ */ + +:root { + --bg: #0b1120; + --bg-soft: #0f1729; + --surface: #131b2e; + --surface-2: #1a2438; + --surface-3: #232f48; + --border: #26324a; + --border-soft: #1e2942; + --text: #e6edf5; + --text-muted: #8b96a8; + --text-dim: #6b7588; + --accent: #22d3aa; + --accent-dim: #16826a; + --accent-soft: rgba(34, 211, 170, 0.12); + --warn: #f5a623; + --warn-soft: rgba(245, 166, 35, 0.12); + --danger: #ef4444; + --danger-soft: rgba(239, 68, 68, 0.12); + --font-mono: "IBM Plex Mono", ui-monospace, monospace; + --font-body: "Inter", system-ui, sans-serif; + --radius: 8px; + --radius-lg: 12px; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.2); + --shadow-lg: 0 4px 12px rgba(0, 0, 0, 0.4), 0 2px 4px rgba(0, 0, 0, 0.2); +} + +* { box-sizing: border-box; } + +html, body { + margin: 0; + padding: 0; + background: var(--bg); + color: var(--text); + font-family: var(--font-body); + -webkit-font-smoothing: antialiased; + font-size: 14px; + line-height: 1.5; +} + +button, input, select, textarea { + font-family: inherit; + font-size: inherit; +} + +a { + color: var(--accent); + text-decoration: none; + transition: color 0.15s ease; +} +a:hover { color: #2ee6bb; } + +/* --- Layout shell --- */ +.shell { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +.topbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 28px; + border-bottom: 1px solid var(--border-soft); + background: var(--surface); + position: sticky; + top: 0; + z-index: 10; +} + +.brand { + font-family: var(--font-mono); + font-weight: 600; + letter-spacing: 0.02em; + font-size: 15px; + display: flex; + align-items: center; + gap: 8px; +} + +.brand .prompt { + color: var(--accent); + font-weight: 700; +} + +.topbar-right { + display: flex; + align-items: center; + gap: 18px; + font-size: 14px; + color: var(--text-muted); +} + +.topbar-right a { + color: var(--text); + padding: 6px 4px; + border-bottom: 2px solid transparent; +} +.topbar-right a:hover { color: var(--accent); } + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 32px 24px 80px; + width: 100%; +} + +.page-title { + font-family: var(--font-mono); + font-size: 24px; + font-weight: 600; + margin: 0 0 6px; + color: var(--text); +} + +.page-sub { + color: var(--text-muted); + margin: 0 0 28px; + font-size: 14px; +} + +/* --- Buttons --- */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 9px 16px; + border-radius: var(--radius); + border: 1px solid var(--border); + background: var(--surface-2); + color: var(--text); + cursor: pointer; + font-size: 14px; + font-weight: 500; + transition: background 0.15s ease, border-color 0.15s ease, transform 0.05s ease; + user-select: none; +} +.btn:hover { background: var(--surface-3); border-color: var(--accent-dim); } +.btn:active { transform: translateY(1px); } +.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } +.btn:disabled { opacity: 0.5; cursor: not-allowed; } + +.btn-primary { + background: var(--accent); + border-color: var(--accent); + color: #06231c; + font-weight: 600; +} +.btn-primary:hover { background: #2ee6bb; border-color: #2ee6bb; } + +.btn-danger { + border-color: rgba(239, 68, 68, 0.4); + color: #ff8a8a; + background: transparent; +} +.btn-danger:hover { background: var(--danger-soft); border-color: var(--danger); } + +.btn-sm { + padding: 6px 10px; + font-size: 12px; +} + +/* --- Cards --- */ +.card { + background: var(--surface); + border: 1px solid var(--border-soft); + border-radius: var(--radius-lg); + padding: 20px; + box-shadow: var(--shadow); +} + +.card + .card { margin-top: 16px; } + +/* --- Forms --- */ +.field { + display: flex; + flex-direction: column; + gap: 6px; + margin-bottom: 16px; +} +.field label { + font-size: 13px; + color: var(--text-muted); + font-weight: 500; +} +.field input, .field select, .field textarea { + background: var(--bg-soft); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 10px 12px; + color: var(--text); + font-size: 14px; + transition: border-color 0.15s ease; +} +.field input:focus, .field select:focus, .field textarea:focus { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.field input::placeholder, .field textarea::placeholder { color: var(--text-dim); } + +/* --- Banners / Errors --- */ +.error-box { + background: var(--danger-soft); + border: 1px solid rgba(239, 68, 68, 0.4); + color: #ffb4b4; + padding: 12px 14px; + border-radius: var(--radius); + font-size: 13px; + margin-bottom: 16px; +} + +.success-box { + background: var(--accent-soft); + border: 1px solid rgba(34, 211, 170, 0.4); + color: var(--accent); + padding: 12px 14px; + border-radius: var(--radius); + font-size: 13px; + margin-bottom: 16px; +} + +/* --- Auth page --- */ +.auth-wrap { + max-width: 380px; + margin: 100px auto; +} +.auth-wrap .card { padding: 32px; } + +/* --- Instance grid --- */ +.instance-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); + gap: 16px; +} + +.instance-card { + background: var(--surface); + border: 1px solid var(--border-soft); + border-radius: var(--radius-lg); + padding: 18px; + display: flex; + flex-direction: column; + gap: 10px; + transition: border-color 0.15s ease, transform 0.1s ease; +} +.instance-card:hover { border-color: var(--border); } + +.status-row { + display: flex; + align-items: center; + gap: 8px; + font-family: var(--font-mono); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--text-muted); +} + +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--text-muted); + flex-shrink: 0; +} +.dot.running { + background: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} +.dot.stopped { background: var(--text-muted); } +.dot.creating { + background: var(--warn); + animation: pulse 1.4s infinite; + box-shadow: 0 0 0 3px var(--warn-soft); +} +.dot.error { background: var(--danger); box-shadow: 0 0 0 3px var(--danger-soft); } + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +.instance-name { + font-size: 16px; + font-weight: 600; + color: var(--text); +} +.instance-name a { + color: var(--text); +} +.instance-name a:hover { color: var(--accent); } + +.instance-meta { + font-family: var(--font-mono); + font-size: 12px; + color: var(--text-muted); + line-height: 1.6; +} + +.instance-actions { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-top: 6px; +} + +/* --- Empty state --- */ +.empty-state { + text-align: center; + padding: 80px 20px; + color: var(--text-muted); + background: var(--surface); + border: 1px dashed var(--border); + border-radius: var(--radius-lg); +} + +/* --- Tabs --- */ +.nav-tabs { + display: flex; + gap: 4px; + margin-bottom: 24px; + border-bottom: 1px solid var(--border-soft); +} +.nav-tab { + padding: 10px 16px; + font-size: 14px; + color: var(--text-muted); + cursor: pointer; + border-bottom: 2px solid transparent; + transition: color 0.15s ease, border-color 0.15s ease; +} +.nav-tab:hover { color: var(--text); } +.nav-tab.active { + color: var(--accent); + border-color: var(--accent); +} + +/* --- Tables --- */ +table.data-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} +table.data-table th, table.data-table td { + text-align: left; + padding: 12px 14px; + border-bottom: 1px solid var(--border-soft); +} +table.data-table tr:last-child td { border-bottom: none; } table.data-table th { color: var(--text-muted); font-weight: 500; - font-size: 12px; + font-size: 11px; text-transform: uppercase; - letter-spacing: 0.04em; + letter-spacing: 0.06em; + background: var(--bg-soft); +} +table.data-table tr:hover td { background: var(--bg-soft); } + +/* --- Inline stat bars --- */ +.metric { + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; + color: var(--text); +} +.metric-bar { + width: 60px; + height: 8px; + background: var(--surface-3); + border-radius: 4px; + overflow: hidden; +} +.metric-bar > span { + display: block; + height: 100%; + border-radius: 4px; + transition: width 0.5s ease; +} +.metric-bar > span.good { background: var(--accent); } +.metric-bar > span.warn { background: var(--warn); } +.metric-bar > span.bad { background: var(--danger); } + +/* --- Badges --- */ +.badge { + font-family: var(--font-mono); + font-size: 11px; + padding: 2px 8px; + border-radius: 100px; + border: 1px solid var(--border); + color: var(--text-muted); + background: var(--bg-soft); } -/* --- Inline stat bars (используются в InstanceCard) --- */ \ No newline at end of file +/* --- Divider --- */ +.hr { + border: none; + border-top: 1px solid var(--border-soft); + margin: 16px 0; +} + +/* --- Mobile --- */ +@media (max-width: 640px) { + .topbar { padding: 12px 16px; } + .container { padding: 24px 16px 64px; } + .instance-grid { grid-template-columns: 1fr; } + .page-title { font-size: 20px; } +}